Exemple de code pour créer un "label" NSTextField?

Dans mon application de bureau Mac OS X, je voudrais créer par programmation une "étiquette" NSTextField qui a le même comportement et les mêmes propriétés qu'une étiquette typique créée dans Interface Builder.

J'utilise habituellement (et j'aime beaucoup) IB, mais dans ce cas, doit être fait par programmation.

Essayez comme je pourrais, je n'arrive pas à trouver la combinaison d'appels de méthode qui produiront par programmation le même comportement label-y qu'un "Label" traîné depuis la bibliothèque IB View palette.

Quelqu'un peut-il fournir ou signaler un exemple de code sur la façon de le faire par programme? Thx.

49
demandé sur Todd Ditchendorf 2009-10-02 07:36:50

6 réponses

Une étiquette est en fait une instance de NSTextField, une sous-classe de NSView. Donc, comme il s'agit D'un NSView, il doit être ajouté à une autre vue.

Voici un code de travail:

- (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];
}
111
répondu Thibault Martin-Lagardette 2013-05-18 14:13:30

MacOS 10.12 et versions ultérieures

À partir de macOS 10.12 (Sierra), il y a trois nouveaux constructeurs NSTextField:

  • NSTextField(labelWithString:), ce que le Commentaire du fichier d'en-tête dit "crée un champ de texte non enveloppant, non modifiable et non sélectionnable qui affiche le texte dans la police système par défaut."

  • NSTextField(wrappingLabelWithString:), ce que le Commentaire du fichier d'en-tête dit " crée un champ de texte sélectionnable, non modifiable et sélectionnable qui affiche le texte dans le système par défaut police."

  • NSTextField(labelWithAttributedString:), ce que le Commentaire du fichier d'en-tête dit "crée un champ de texte non modifiable et non sélectionnable qui affiche le texte attribué. Le mode de saut de ligne de ce champ est déterminé par L'attribut NSParagraphStyle de la chaîne attribuée."

J'ai testé ceux qui prennent une chaîne simple (non attribuée), et ils créent des champs de texte qui sont similaires, mais pas exactement les mêmes que, les champs de texte créés dans un storyboard ou xib.

Le la différence importante est que les deux constructeurs créent un champ de texte avec textBackgroundColor (normalement Blanc pur) comme couleur d'arrière-plan, tandis que le champ de texte storyboard utilise controlColor (normalement environ 90% Blanc).

Sans importance, les deux constructeurs définissent également leurs polices en appelant NSFont.systemFont(ofSize: 0) (ce qui produit un objet NSFont différent de mon code ci-dessous, mais ils enveloppent la même police de texte de base sous-jacente).

Le wrappingLabelWithString: constructeur définit le champ isSelectable à true. (Ceci est documenté dans l'en-tête fichier.)


MacOS 10.11 et versions antérieures

J'ai comparé quatre instances NSTextField: une créée en faisant glisser un "Label" vers un storyboard, une autre créée en faisant glisser un "Wrapping Label" vers un storyboard, et deux dans le code. Ensuite, j'ai soigneusement modifié les propriétés des étiquettes créées par le code jusqu'à ce que toutes leurs propriétés soient exactement les mêmes que les étiquettes créées par le storyboard. Ces deux méthodes sont le résultat:

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
    }

}

Si vous utilisez l'une de ces méthodes, n'oubliez pas de définir le champ frame, ou désactiver son translatesAutoresizingMaskIntoConstraints et ajouter des contraintes.


Voici le code que j'ai utilisé pour comparer les différents champs de texte, au cas où vous voudriez vérifier:

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: " "))
        }
    }
}
16
répondu rob mayoff 2018-05-12 18:20:40

Cela peut être difficile à obtenir. Je n'ai pas la recette d'une réplique exacte à portée de main, mais quand j'ai été coincé dans une situation similaire, voici ce que je fais:

  1. Créez un élément D'interface utilisateur dans IB.
  2. Ajoutez une sortie à partir de ma classe de contrôleur.
  3. pause dans gdb dans awakeFromNib ou autre.
  4. à partir de l'invite gdb, "p * whateverOutlet"... cela vous montrera le contenu de la structure C de l'étiquette NSTextField configurée par IB.

En regardant toute la myriade les valeurs là-dedans, vous pouvez obtenir beaucoup de suppositions sur ce que vous négligez de définir. Habituellement, il finit par être une combinaison magique de paramètres de lunette et de bordure, qui vous amène où vous voulez être.

8
répondu danielpunkass 2009-10-02 03:54:41

Vous pouvez essayer d'utiliser nib2objc pour obtenir toutes les propriétés définies par IB

5
répondu g-Off 2009-10-02 04:07:28

Plus précisément, vous voudrez setBordered:NO, et définir le style de lunette à ce que ce style de lunette est que j'ai oublié. Aussi setEditable:NO, et éventuellement setSelectable:NO. Cela devrait suffire.

2
répondu Steven Degutis 2016-03-18 20:32:56

AppKit démonté dans 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
répondu Stream 2017-03-01 15:31:47