wake-up-neo.net

Beispielcode zum Erstellen eines NSTextField "Labels"?

In meiner Mac OS X-Desktopanwendung möchte ich programmgesteuert ein NSTextField-Label erstellen, das dasselbe Verhalten und dieselben Eigenschaften aufweist wie ein typisches Label, das im Interface Builder erstellt wird.

Normalerweise verwende ich IB (und mag es auch sehr), aber in diesem Fall wird must programmgesteuert gemacht.

Ich versuche es zu versuchen, die Kombination von Methodenaufrufen zu finden, die programmgesteuert dasselbe Label-y-Verhalten ergeben wie ein "Label", das aus der Palette IB View Library gezogen wird.

Kann jemand einen Beispielcode angeben oder aufzeigen, wie dies programmgesteuert geschieht? Vielen Dank.

49

Ein Label ist eigentlich eine Instanz von NSTextField , einer Unterklasse von NSView. Da es sich um eine NSView handelt, muss sie einer anderen Ansicht hinzugefügt werden.

Hier ist ein Funktionscode:

- (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 und später

Beginnend mit macOS 10.12 (Sierra) gibt es drei neue NSTextField-Konstruktoren:

  • NSTextField(labelWithString:), der Kommentar der Header-Datei besagt "Erzeugt ein nicht-bearbeitbares, nicht bearbeitbares, nicht auswählbares Textfeld, das Text in der Standardsystemschriftart anzeigt." 

  • NSTextField(wrappingLabelWithString:): In dem Kommentar der Header-Datei heißt es: "Erstellt ein nicht bearbeitbares, wählbares Textfeld, in dem Text in der Standardsystemschriftart angezeigt wird."

  • NSTextField(labelWithAttributedString:), der Kommentar der Header-Datei besagt "Erzeugt ein nicht bearbeitbares, nicht auswählbares Textfeld, in dem zugeordneter Text angezeigt wird. Der Zeilenumbruchmodus dieses Felds wird durch das NSParagraphStyle-Attribut der Attributzeichenfolge bestimmt. "

Ich habe diejenigen getestet, für die eine einfache Zeichenfolge (nicht zugeordnete Zeichenfolge) gilt. Sie erstellen Textfelder, die den in einem Storyboard oder Xib erstellten Textfeldern ähneln, jedoch nicht genau diesen.

Der wichtige Unterschied besteht darin, dass beide Konstruktoren ein Textfeld mit textBackgroundColor (normalerweise reines Weiß) als Hintergrundfarbe erstellen, während das Storyboard-Textfeld controlColor (normalerweise etwa 90% Weiß) verwendet.

Es ist unwichtig, dass beide Konstruktoren auch ihre Schriftarten festlegen, indem sie NSFont.systemFont(ofSize: 0) aufrufen (wodurch ein anderes NSFont-Objekt als mein Code unten erzeugt wird, der jedoch dieselbe zugrunde liegende Kerntextschriftart umgibt).

Der wrappingLabelWithString:-Konstruktor setzt die isSelectable des Feldes auf true. (Dies ist in der Header-Datei dokumentiert.)


macOS 10.11 und früher

Ich habe vier NSTextField-Instanzen verglichen: eine Instanz, die durch Ziehen eines "Labels" in ein Storyboard erstellt wurde, eine andere durch Ziehen eines "Wrapping-Labels" in ein Storyboard, und zwei im Code. Dann habe ich die Eigenschaften der mit Code erstellten Beschriftungen sorgfältig geändert, bis alle ihre Eigenschaften genau den von Storyboard erstellten Beschriftungen entsprachen. Diese beiden Methoden sind das Ergebnis:

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
    }

}

Wenn Sie eine dieser Methoden verwenden, vergessen Sie nicht, den Rahmen Ihres Felds festzulegen oder die Variable translatesAutoresizingMaskIntoConstraints zu deaktivieren und Einschränkungen hinzuzufügen.


Hier ist der Code, den ich zum Vergleichen der verschiedenen Textfelder verwendet habe, falls Sie prüfen möchten:

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

Das kann schwierig sein, um recht zu bekommen. Ich habe nicht das Rezept für eine exakte Kopie zur Hand, aber wenn ich in einer ähnlichen Situation festgefahren bin, mache ich Folgendes:

  1. Legen Sie ein UI-Element in IB an.
  2. Fügen Sie einen Auslass aus meiner Controller-Klasse hinzu.
  3. Brechen Sie in wakeFromNib oder was auch immer in gdb ein.
  4. Über die gdb-Eingabeaufforderung "p * whateverOutlet" ... werden die C struct-Inhalte des von IB eingerichteten Labels NSTextField angezeigt.

Wenn Sie sich all die unzähligen Werte ansehen, können Sie viele Vermutungen darüber erhalten, was Sie beim Setzen nicht vernachlässigen. In der Regel ist dies eine magische Kombination aus Rahmen- und Randeinstellungen, die Sie dahin bringt, wo Sie hin möchten.

8
danielpunkass

Sie können versuchen, nib2objc zu verwenden, um alle Eigenschaften zu erhalten, die von IB festgelegt werden

5
g-Off

Insbesondere möchten Sie setBordered:NO und den Blendenstil auf den Blendenstil setzen, den ich vergessen habe. Auch setEditable:NO und optional setSelectable:NO. Das sollte ausreichen.

2
Steven Degutis

Disassembliertes AppKit in Objective-C:

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
0
Vadim