Skip to content

NSTextField

Ken Harris edited this page Jan 26, 2021 · 4 revisions

NSTextField is a "text field", which can mean either a label or an entry field. (UIKit divides these uses into UILabel/UITextField.)

Two oddities to be aware of:

  1. There's only one field editor per window.

  2. Despite NSCell being in the process of being deprecated, NSTextField still (as of 10.15) uses NSTextFieldCell.

Sub-pixel anti-aliasing is removed in 10.14, so you no longer have to worry about weird differences in how text is drawn.


How to be informed when the text of your field changes? There's several ways, and they're all slightly different:

  • You can use NotificationCenter to watch for NSControl.textDidChangeNotification. This only seems to report user-typed edits! Setting textfield.stringValue programmatically won't trigger this. (Some good news: as of macOS 10.11 / iOS 9.0, these are removed automatically at object deallocation time.)

  • You can be conform to NSTextFieldDelegate, and assign yourself as the delegate. (All of its methods are optional. You might want controlTextDidChange(_:) here.) This uses notifications, so you'll only get user-typed edits. Also, note that there can be only one of these per NSTextField, so you can use it if you're the NSTextField, but you probably don't want to use this to watch some other object. (Delegates are, of course, weak, so you'll never need to de-register these.)

  • You can use KVO, like textfield.addObserver(me, forKeyPath: "stringValue", options: [.new, .old, .initial], context: nil). This reports on both user-typed edits and programmatic edits to stringValue. Be sure to call removeObserver(_:forKeyPath:context:) when you're done.

  • Bindings use KVO, so they should report both kinds of changes (untested by me). Note that if you don't set NSBindingOption.continuouslyUpdatesValue, you'll only get changes reported from user edits after the user focuses a different control.

(The documentation is certainly confusing. controlTextDidChange(:_) says "This method is invoked when text in a control such as a text field or form changes", which suggests it's for any change, while the notification which generates that delegate method, textDidChangeNotification, says it's for "The field editor of the edited cell", which implies it's only for user edits to an editable field.)


There's a convenience init -labelWithString which looks handy, but it's one of the many "No overview available" methods. I disassembled it, and the brief screenshot in WWDC 2016/203 is actually mostly accurate, with a couple caveats:

  • It also sets lineBreakMode (on its NSCell?), continuous, and the hugging and compression resistance priorities.
  • It uses an NSFont factory method, as you can see, with argument 0. A non-positive argument means "default size", but NSFont's functionality here is buggy in similar cases (menuFont(ofSize:) returns the wrong size, in 10.15 and since at least 10.12), so I'm not sure I'd trust it.

Of course, this is all implementation detail, as of 10.15 -- nowhere did the authors say this is what it does, or will do in the future.


NSTextField has a field drawsBackground. (-labelWithString sets it to false.) It probably doesn't do what you think it does. The documentation says:

If YES, the text field’s cell draws a background; if NO, it draws nothing behind the text.

In fact, if NO, then the background is opaque gray, regardless of the control's backgroundColor. If YES, then the control's backgroundColor is used. This means if you want a transparent background, you need to set drawsBackground=true and backgroundColor=.clear.

Clone this wiki locally