用于创buildNSTextField“标签”的示例代码?

在我的桌面Mac OS X应用程序中,我想以编程方式创build一个NSTextField“标签”,它具有与在Interface Builder中创build的典型标签相同的行为和属性。

我通常使用(和非常像)IB,但在这种情况下,它必须以编程方式完成。

尝试我可能,我似乎无法find方法调用的组合,将编程方式产生相同的label-y行为作为从IB视图库调色板拖动“标签”。

任何人都可以提供或指出一些示例代码如何以编程方式做到这一点? 谢谢。

标签实际上是NSView的一个子类NSTextField的一个实例。 所以,因为它是一个NSView,它必须被添加到另一个视图。

这是一个工作代码:

- (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]; } 

macOS 10.12更新

从macOS 10.12开始 ,有三个新的NSTextField构造函数:

  • NSTextField(labelWithString:) ,其中头文件注释表示“创build一个非包装,不可编辑,不可选的文本字段,显示默认系统字体中的文本。

  • NSTextField(wrappingLabelWithString:) ,其中头文件注释说:“创build一个包装,不可编辑,可选的文本字段,显示默认系统字体中的文本。

  • NSTextField(labelWithAttributedString:) ,其头文件注释说:“创build一个不可编辑的,不可选的文本字段,显示属性的文本。 该字段的换行符模式由属性string的NSParagraphStyle属性决定。

我testing了那些带有普通string(非属性string)的string,他们创build的文本字段与在故事板或xib中创build的文本字段类似,但不完全相同。

最重要的区别是两个构造函数都创build了一个textBackgroundColor (通常是纯白色)作为背景颜色的文本字段,而storyboard文本字段使用controlColor (通常约为90%的白色)。

不重要的是,这两个构造函数也通过调用NSFont.systemFont(ofSize: 0) (它产生一个不同的NSFont对象比我上面的代码,但他们包装相同的底层核心文本字体)设置他们的字体。

wrappingLabelWithString:构造函数将字段的isSelectabletrue 。 (这在头文件中有记载)


我比较了四个NSTextField实例:一个是通过拖拽一个“Label”到故事板创build的,另一个是通过拖拽一个“Wrapping Label”到故事板创build的,另外两个是用代码创build的。 然后,我仔细修改了代码创build的标签的属性,直到它们的所有属性都与故事板创build的标签完全相同。 这两个方法是结果:

 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 } } 

如果您使用这些方法之一,不要忘记设置您的字段的框架,或closures其translatesAutoresizingMaskIntoConstraints并添加约束。


这里是我用来比较不同的文本字段的代码,如果你想检查:

 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: " ")) } } } 

这可能是棘手的,以得到正确的。 我没有一个准确的副本方便的食谱,但是当我陷入了类似的情况,这就是我所做的:

  1. 在IB中创build一个UI元素。
  2. 从我的控制器类添加一个sockets。
  3. 在awakeFromNib或其他方面的gdb中断。
  4. 在gdb提示符下,“p * whateverOutlet”…这将显示IB设置的标签NSTextField的C结构内容。

通过查看那里所有的无数值,你可以得到很多关于你忽略的东西的猜测。 通常情况下,它最终会成为一些边框和边框设置的魔法组合,让您可以在任何想要的位置find。

您可以尝试使用nib2objc来获取IB设置的所有属性

具体来说,您将要设置setBordered:NO ,并将挡板样式设置为任何我忘记的挡板样式。 另外setEditable:NO ,可选setSelectable:NO 。 这应该够了。

在Objective-C中反汇编的AppKit:

 BOOL TMPSierraOrLater() { static BOOL result = NO; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ result = [NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){ 10, 12, 0 }]; }); return result; } @implementation NSTextField (TMP) + (instancetype)TMP_labelWithString:(NSString *)stringValue { if (TMPSierraOrLater()) { return [self labelWithString:stringValue]; } NSParameterAssert(stringValue); NSTextField *label = [NSTextField TMP_newBaseLabelWithoutTitle]; label.lineBreakMode = NSLineBreakByClipping; label.selectable = NO; [label setContentHuggingPriority:(NSLayoutPriorityDefaultLow + 1) forOrientation:NSLayoutConstraintOrientationHorizontal]; [label setContentHuggingPriority:NSLayoutPriorityDefaultHigh forOrientation:NSLayoutConstraintOrientationVertical]; [label setContentCompressionResistancePriority:NSLayoutPriorityDefaultHigh forOrientation:NSLayoutConstraintOrientationHorizontal]; [label setContentCompressionResistancePriority:NSLayoutPriorityDefaultHigh forOrientation:NSLayoutConstraintOrientationVertical]; label.stringValue = stringValue; [label sizeToFit]; return label; } + (instancetype)TMP_wrappingLabelWithString:(NSString *)stringValue { if (TMPSierraOrLater()) { return [self wrappingLabelWithString:stringValue]; } NSParameterAssert(stringValue); NSTextField *label = [NSTextField TMP_newBaseLabelWithoutTitle]; label.lineBreakMode = NSLineBreakByWordWrapping; label.selectable = YES; [label setContentHuggingPriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; [label setContentHuggingPriority:NSLayoutPriorityDefaultHigh forOrientation:NSLayoutConstraintOrientationVertical]; [label setContentCompressionResistancePriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; [label setContentCompressionResistancePriority:NSLayoutPriorityDefaultHigh forOrientation:NSLayoutConstraintOrientationVertical]; label.stringValue = stringValue; label.preferredMaxLayoutWidth = 0; [label sizeToFit]; return label; } + (instancetype)TMP_labelWithAttributedString:(NSAttributedString *)attributedStringValue { if (CRKSierraOrLater()) { return [self labelWithAttributedString:attributedStringValue]; } NSParameterAssert(attributedStringValue); NSTextField *label = [NSTextField TMP_newBaseLabelWithoutTitle]; [label setContentHuggingPriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; [label setContentHuggingPriority:NSLayoutPriorityDefaultHigh forOrientation:NSLayoutConstraintOrientationVertical]; [label setContentCompressionResistancePriority:NSLayoutPriorityDefaultLow forOrientation:NSLayoutConstraintOrientationHorizontal]; [label setContentCompressionResistancePriority:NSLayoutPriorityDefaultHigh forOrientation:NSLayoutConstraintOrientationVertical]; label.attributedStringValue = attributedStringValue; [label sizeToFit]; return label; } #pragma mark - Private API + (instancetype)TMP_newBaseLabelWithoutTitle { NSTextField *label = [[self alloc] initWithFrame:CGRectZero]; label.textColor = NSColor.labelColor; label.font = [NSFont systemFontOfSize:0.0]; label.alignment = NSTextAlignmentNatural; label.baseWritingDirection = NSWritingDirectionNatural; label.userInterfaceLayoutDirection = NSApp.userInterfaceLayoutDirection; label.enabled = YES; label.bezeled = NO; label.bordered = NO; label.drawsBackground = NO; label.continuous = NO; label.editable = NO; return label; } @end