When does backgroundStyle on NSTextfield change its textColor?

I am trying to understand how an NSTextField reacts to changes of its cell's backgroundStyle.


The reason is that I want to use the automatic handling of the text color be used when I set the backgroundStyle of the text field that's inside a table cell.


From what I have gathered so far, the textfield's cell has a backgroundStyle property and when this is set, and the textColor is `labelColor` (or any of the other control colors) the text color should be automatically updated to be fitting for the backgroundStyle.


Given a tableView with a custom row class and a custom cell:


class RowView: NSTableRowView {
    override func drawSelection(in dirtyRect: NSRect) {
        if isSelected {
            NSColor.white.set()
            var rects: UnsafePointer<NSRect>? = nil
            var count: Int = 0
            self.getRectsBeingDrawn(&rects, count: &count)
            for i in 0..<count {
                let rect = NSIntersectionRect(bounds, rects![i])
                __NSRectFillUsingOperation(rect, NSCompositingOperation.sourceOver)
            }
        }
    }
    
    override var interiorBackgroundStyle: NSView.BackgroundStyle {
        return isSelected ? .light : .dark
    }
}

and

class CustomCellView: NSTableCellView {
    @IBOutlet weak var label: NSTextField!
    override var backgroundStyle: NSView.BackgroundStyle {
        didSet {
            Swift.print("Style set to \(backgroundStyle == .light ? "light" : "dark")")
            Swift.print("Label Style set to \(label.cell!.backgroundStyle == .light ? "light" : "dark")")
        }
    }
}


I would expect the label to be drawn in black, when I select the row.


I can see that the backgroundStyle is forwarded correctly when I select the row, but the label doesn't change color at all. In fact, being seemingly blended with my white selection, the label disappears entirely.


Why is the label color not changing?


P.S.: I know that I can manually set the label's color based on the background style of the cell. That's not what I am asking. I want to use the convenience of the `controlTextColor`s or at least understand why they are not working in this case. (They do seem to work if I don't draw my custom selection in the row and use the standard blue selection)

Replies

I don't understand the purpose of override var backgroundStyle.

If you show complete code, it does nothing but log.


What happens if you remove this override ?


Did you notice in documentation about backgroundStyle


The default implementation automatically forwards calls to all subviews that implement

setBackgroundStyle:
or are an NSControl, which have
NSCell
classes that respond to
backgroundStyle
.

I am not overriding the implementation of backgroundStyle, but adding a property observer.

This property observer only confirms, that the automatic forwarding is properly called, so that is not an issue. It does (as you point out) nothing but log this.


So the textfield does get a different background style set, yet doesn't change the color of the text at all. That's the question.

Which version of OSX ?


I noted in doc for 10.14

https://developer.apple.com/documentation/macos_release_notes/macos_mojave_10_14_release_notes/appkit_release_notes_for_macos_10_14

To avoid drawing issues, apps linked on macOS SDKs prior to 10.14 are opted out of the tinting effect when a table view's background color is set to

controlBackgroundColor
and the table view overrides
isOpaque
.

I am testing this on 10.14 with the latest SDK.


I don't think the quote you sent is related. It is not about the table's background color.

In my (limited) understanding it should be like this:


- The row propagates the `backgroundStyle` to any cell in it (and by default those cells propagate this to appropriate subviews)

- An NSTextField that has its textColor set to any of the "automatic" control colors will redraw when the backgroundStyle changes and the control color is different for dark and light backgroud style.


Come to think of it, there is one more component at play here: The systems Dark Mode.


When I switch to light system appearance, I can actually see a difference in label colors. Then by default the label is invisible against the white cell background (the default background for light appearance). That's because I return ".dark" for unselected.

Once I select the cell in light appearance the label suddenly becomes visible because (I assume) now the ".light" backgroundStyle makes the label display a dark gray color.


Do you know what I mean?


Would be cool if an AppKit Engineer could chime in 😉

Sorry not be able to help more.


There is a WWDC 2018 about dark mode that dealt with similar issues.


https://developer.apple.com/videos/play/wwdc2018/218/

starts at about 8´


Hope that may help

Ok it seems the problem is that in dark mode (which I was using) the `normal` and `emphasized` styles (which are the new semantic styles that are set) are both bright (no matter what the actual color is that they are placed on).

So when I set my selection to white, it doesn't matter to the textfield, because it always has a bright text color.


My question now would be if there is any way to make this work correctly automatically.


If not, the follow-up question(s) would be:


Given I have to set the colors manually whenever the backgroundStyle changes, what is the most elegant way to do this?


- I can use the asset catalog to have an NSColor that has variants, but I would actually prefer to have that in code.

- I could have a textfield cell subclass that handles setting the color, but then I'd need to set this in all specific textfields and have different subclasses for each color variation.


Any ideas?

This magic is frustrating when it does not work.

I have a SwiftUI view (PhysicalSize) wrapped in NSHostingView that is placed inside a NSTableCellView.

I have set a custom NSTableRowView on the NSTableView and AppKit is setting the proper isEmphasized status when the row is selected. Good.

Then I'm setting the backgroundStyle of the cells to .emphasized Good.

However the swift UI is absolutely unaware, that we are being drawn in highlighted mode and the Text color does invert. Not good.

Can some apple genius chime in here.

struct PhysicalSize: View {
    var file: File

    var body: some View {
        HStack {
            Spacer()
            Text(file.physicalSize.compactFormatted)
                .font(.subheadline)
        }
    }
}