In my desktop Mac OS X app, I'd like to programatically create a NSTextField "label" which has the same behavior and properties as a typical label created in Interface Builder.
I usually use (and very much like) IB, but in this case it must be done programatically.
Try as I might, I can't seem to find the combination of method calls that will programatically produce the same label-y behavior as a "Label" dragged from the IB View Library palette.
Can anyone provide or point out some example code of how to do this programatically? Thx.
A label is actually an instance of NSTextField, a subclass of NSView. So, since it is a NSView, it has to be added to another view.
Here's a working code:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { NSTextField *textField; textField = [[NSTextField alloc] initWithFrame:NSMakeRect(10, 10, 200, 17)]; [textField setStringValue:@"My Label"]; [textField setBezeled:NO]; [textField setDrawsBackground:NO]; [textField setEditable:NO]; [textField setSelectable:NO]; [view addSubview:textField]; }
Starting with macOS 10.12 (Sierra), there are three new NSTextField
constructors:
NSTextField(labelWithString:)
, which the header file comment says “Creates a non-wrapping, non-editable, non-selectable text field that displays text in the default system font.”
NSTextField(wrappingLabelWithString:)
, which the header file comment says “Creates a wrapping, non-editable, selectable text field that displays text in the default system font.”
NSTextField(labelWithAttributedString:)
, which the header file comment says “Creates a non-editable, non-selectable text field that displays attributed text. The line break mode of this field is determined by the attributed string's NSParagraphStyle attribute.”
I tested the ones that take a plain (non-attributed string), and they create text fields that are similar to, but not precisely the same as, the text fields created in a storyboard or xib.
The important difference is that both constructors create a text field with textBackgroundColor
(normally pure white) as its background color, while the storyboard text field uses controlColor
(normally about 90% white).
Unimportantly, both constructors also set their fonts by calling NSFont.systemFont(ofSize: 0)
(which produces a different NSFont
object than my code below, but they wrap the same underlying Core Text font).
The wrappingLabelWithString:
constructor sets the field's isSelectable
to true
. (This is documented in the header file.)
I compared four NSTextField
instances: one created by dragging a “Label” to a storyboard, another created by dragging a “Wrapping Label” to a storyboard, and two in code. Then I carefully modified properties of the code-created labels until all their properties were exactly the same as the storyboard-created labels. These two methods are the result:
extension NSTextField { /// Return an `NSTextField` configured exactly like one created by dragging a “Label” into a storyboard. class func newLabel() -> NSTextField { let label = NSTextField() label.isEditable = false label.isSelectable = false label.textColor = .labelColor label.backgroundColor = .controlColor label.drawsBackground = false label.isBezeled = false label.alignment = .natural label.font = NSFont.systemFont(ofSize: NSFont.systemFontSize(for: label.controlSize)) label.lineBreakMode = .byClipping label.cell?.isScrollable = true label.cell?.wraps = false return label } /// Return an `NSTextField` configured exactly like one created by dragging a “Wrapping Label” into a storyboard. class func newWrappingLabel() -> NSTextField { let label = newLabel() label.lineBreakMode = .byWordWrapping label.cell?.isScrollable = false label.cell?.wraps = true return label } }
If you use one of these methods, don't forget to set your field's frame, or turn off its translatesAutoresizingMaskIntoConstraints
and add constraints.
Here is the code I used to compare the different text fields, in case you want to check:
import Cocoa class ViewController: NSViewController { @IBOutlet var label: NSTextField! @IBOutlet var multilineLabel: NSTextField! override func loadView() { super.loadView() } override func viewDidLoad() { super.viewDidLoad() let codeLabel = NSTextField.newLabel() let codeMultilineLabel = NSTextField.newWrappingLabel() let labels = [label!, codeLabel, multilineLabel!, codeMultilineLabel] for keyPath in [ "editable", "selectable", "allowsEditingTextAttributes", "importsGraphics", "textColor", "preferredMaxLayoutWidth", "backgroundColor", "drawsBackground", "bezeled", "bezelStyle", "bordered", "enabled", "alignment", "font", "lineBreakMode", "usesSingleLineMode", "formatter", "baseWritingDirection", "allowsExpansionToolTips", "controlSize", "highlighted", "continuous", "cell.opaque", "cell.controlTint", "cell.backgroundStyle", "cell.interiorBackgroundStyle", "cell.scrollable", "cell.truncatesLastVisibleLine", "cell.wraps", "cell.userInterfaceLayoutDirection" ] { Swift.print(keyPath + " " + labels.map({ ($0.value(forKeyPath: keyPath) as? NSObject)?.description ?? "nil" }).joined(separator: " ")) } } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With