How to invert text colour of selected NSTableView row
Asked Answered
M

3

10

I have an NSTableView in which I'm changing the colour of the text in a particular column, but when a row is selected, the text does not change to a more appropriate colour so that it's readable.

Finder's Date Modified, Size, and Kind columns have grey text, and when you select a file/folder row, the grey text changes to white (so that it's readable within the blue highlight).

I can't find a magic checkbox on XCode to enable this behaviour by default, so does anybody know how I might achieve the same effect?

I'm using Swift in XCode 6.3.

Thanks.

Makell answered 24/4, 2015 at 23:35 Comment(0)
P
25

You don't say what view or view hierarchy you're using for your cells. You also don't say how or where you're setting the text fields' color or to what color, specifically.

When a row is selected, the row automatically computes its interiorBackgroundStyle. It also sets the backgroundStyle of the cell view if it responds to -setBackgroundStyle: or is an NSControl with a cell which responds to that.

If your cell view is an instance of NSTableCellView, it forwards the background style to all of its subviews which meet the same criteria. If you use a different container view as your cell view and you want the background style forwarded along like this, you would have to implement that yourself in your view class.

An NSTextField's cell (an NSTextFieldCell) responds to -setBackgroundStyle: and so has its background style set automatically by the above mechanisms. The text field cell will automatically change its text color to white if its textColor is one of the standard control colors (e.g. NSColor.controlTextColor()), but won't do so if you assign a non-standard color. So, if you're setting a specific color for your text, you are responsible for changing that when the background style changes.

You can use a subclass of NSTableCellView and add a property observer (didSet) for the backgroundStyle property. That can change the text field's textColor depending on the style that was set. For example, you can use your custom color if the background style is not .Dark or use the normal text field color NSColor.controlTextColor() if it is .Dark (so that the text field will actually display it as white).

You could also use a subclass of NSTextFieldCell for your text field and do the same sort of thing. Or override drawInteriorWithFrame(_:inView:) to draw with a different text color depending on the background style.

Physique answered 25/4, 2015 at 1:53 Comment(2)
In the tableView:viewForTableColumn:row: delegate method I'm doing this to set the colour of the text field in my view-based cell: cell.textField!.textColor = NSColor.redColor(). Cells set to blackColor() invert just fine, but redColor() does not. Your answer will take some time to figure out, so thanks for giving me some ideas to look into.Makell
Dude, I’ll check, but if this works you’re my savior!! AppKit is so weird when coming from UIKit :)Williford
M
8

With the help of Ken's response (above) I was able to get it to work. Here's a rough draft that does what I want it to:

import Cocoa

class CustomTextFieldCell: NSTextFieldCell {

    // When the background changes (as a result of selection/deselection) switch appropriate colours
    override var backgroundStyle: NSBackgroundStyle {
        didSet {
            if (backgroundStyle == NSBackgroundStyle.Dark) {
                if self.textColor == NSColor.redColor() {
                    self.textColor = NSColor.yellowColor()
                }
            } else if (backgroundStyle == NSBackgroundStyle.Light) {
                if (self.textColor == NSColor.yellowColor()) {
                    self.textColor = NSColor.redColor()
                }
            }
        }
    }

    // When the colour changes, switch to a better alternative for the cell's current background
    override var textColor: NSColor? {
        didSet {
            if let colour = self.textColor {
                if backgroundStyle == NSBackgroundStyle.Dark {
                    if self.textColor == NSColor.redColor() {
                        self.textColor = NSColor.yellowColor()
                    }
                } else if backgroundStyle == NSBackgroundStyle.Light {
                    if (self.textColor == NSColor.yellowColor()) {
                        self.textColor = NSColor.redColor()
                    }
                }
            }
        }
    }

}

If I set my CustomTextFieldCell as the custom class for my table view cell in the identity inspector, it works. I needed to add a property observer for textColor as well so that rows which are currently highlighted get the same treatment. I may alter it now so that it's not hard coded but this demonstrates the concept.

Thanks Ken.

Makell answered 25/4, 2015 at 5:9 Comment(0)
A
1

If you use system colors like NSColor.labelColor, the text color invertion will happen automatically for selected cells. If you want to use another color for unselected text, you can use code like this in your custom NSTableCellView class:

override var backgroundStyle: NSView.BackgroundStyle {
    willSet {
        textField.textColor = newValue == .emphasized ? .labelColor : .secondaryLabelColor
    }
}

In this Swift example, the selected text color is .labelColor, otherwise it is .secondaryLabelColor.

Arciform answered 18/2, 2021 at 12:4 Comment(1)
This was very helpful. For some reason, my table cell was using Text Color instead of Control Color. Once I changed it, the color inversion on selection worked as expected.Mattoid

© 2022 - 2024 — McMap. All rights reserved.