Marc Fearby
Marc Fearby

Reputation: 1384

How to invert text colour of selected NSTableView row

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.

Upvotes: 10

Views: 4498

Answers (3)

Ely
Ely

Reputation: 9141

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.

Upvotes: 1

Marc Fearby
Marc Fearby

Reputation: 1384

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.

Upvotes: 8

Ken Thomases
Ken Thomases

Reputation: 90611

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.

Upvotes: 25

Related Questions