Search code examples
cocoanstextviewmacos-darkmode

NSTextView draws black on almost-black in Dark Mode


When I build my macOS app in Dark Mode, some of my text views (NSTextView) render black text on an almost-black background. In Interface Builder, in the Attributes Inspector, the "Text Color" is set to the system "Default (Text Color)", which I believe is correct. Indeed, in Interface Builder, this text renders white in Dark Mode and black in Light Mode, as desired.

I've searched my code for any lines where I might be programmatically setting the text color in this view to black, but can't find any. Why is my text always black?


Solution

  • I noticed that the errant text views have their "attributed string" bound, with Cocoa Bindings, to methods which return plain, not attributed NSString objects. I probably did this because I was lazy when I wrote this app years ago, and it worked fine. This mismatch turned out to be the problem. The fix is to modify these methods to return a NSAttributedString, with an attribute dictionary containing the key/value pair

    NSForegroundColorAttributeName : NSColor.controlTextColor
    

    Probably what happened is that Cocoa was designed to do what you probably want when a attributed string binding gets a non-attributed string. Instead of barfing an exception, Cocoa applies some "default" attributes, which include the black text color which has been the macOS default since 1984 – totally sensible until Dark Mode came along! Well, it might have been nice of Apple to change this default from black to controlTextColor, but apparently they did not.

    Conclusion: We can no longer get away with binding the attributed string of a text view to a plain non-attributed string.

    Or, you can use the answer of @Ely and bind to value. But if you try that, and do not see a value binding in the Bindings Inspector, but do see a data binding, it is because of these remarks in the NSTextField documentation:

    [value] binding is only available when the NSTextView is configured to display using as a single font.

    and later

    [data] binding is only available when the NSTextView is configured to allow multiple fonts.

    It turns what they mean by configured to allow multiple fonts is that, in the Attributes inspector, the Allows Rich Text checkbox is on. Conversely, configured to display using as a single font means that the Allows Rich Text checkbox is off.