Пример кода для создания "метки" NSTextField? - PullRequest
50 голосов
/ 02 октября 2009

В моем настольном приложении Mac OS X я хотел бы программно создать «метку» NSTextField, которая будет иметь то же поведение и свойства, что и обычная метка, созданная в Interface Builder.

Я обычно использую (и очень люблю) IB, но в этом случае должно быть сделано программно.

Как ни старайся, я не могу найти комбинацию вызовов методов, которые программно будут вызывать такое же поведение label-y, как "Метка", перетаскиваемая из палитры IB View Library.

Может ли кто-нибудь предоставить или указать пример кода, как это сделать программно? Thx.

Ответы [ 6 ]

114 голосов
/ 02 октября 2009

Метка на самом деле является экземпляром NSTextField , подклассом NSView. Таким образом, поскольку это 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];
}
18 голосов
/ 16 декабря 2016

macOS 10.12 и новее

Начиная с macOS 10.12 (Sierra), есть три новых NSTextField конструктора:

  • NSTextField(labelWithString:), что в комментарии к заголовочному файлу гласит: «Создает неразворачиваемое, не редактируемое, не выбираемое текстовое поле, которое отображает текст системным шрифтом по умолчанию».

  • NSTextField(wrappingLabelWithString:), который в комментарии к заголовочному файлу гласит: «Создает переносимое, не редактируемое, выбираемое текстовое поле, отображающее текст системным шрифтом по умолчанию».

  • NSTextField(labelWithAttributedString:), что в комментарии к заголовочному файлу гласит «Создает не редактируемое, не выбираемое текстовое поле, в котором отображается атрибутивный текст. Режим разрыва строки в этом поле определяется атрибутом NSParagraphStyle приписанной строки. ”

Я протестировал те, которые берут простую (не приписываемую строку), и они создают текстовые поля, которые похожи, но не точно совпадают с текстовыми полями, созданными в раскадровке или XIB.

Важным отличием является то, что оба конструктора создают текстовое поле с textBackgroundColor (обычно чисто белым) в качестве цвета фона, тогда как в текстовом поле раскадровки используется controlColor (обычно около 90% белого).

Неважно, что оба конструктора также устанавливают свои шрифты, вызывая NSFont.systemFont(ofSize: 0) (который создает объект NSFont, отличный от моего кода ниже, но они обертывают тот же основной шрифт Core Text).

Конструктор wrappingLabelWithString: устанавливает isSelectable поля в true. (Это задокументировано в заголовочном файле.)


macOS 10.11 и ранее

Я сравнил четыре NSTextField экземпляра: один, созданный перетаскиванием «Метка» на раскадровку, другой созданный перетаскиванием «Оберточной метки» на раскадровку, и два в коде. Затем я тщательно изменил свойства меток, созданных в коде, чтобы все их свойства были точно такими же, как метки, созданные в раскадровке. Эти два метода являются результатом:

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
    }

}

Если вы используете один из этих методов, не забудьте установить фрейм поля или отключить 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: " "))
        }
    }
}
8 голосов
/ 02 октября 2009

Это может быть сложно, чтобы получить право. У меня нет рецепта для точной реплики, но когда я застрял в подобной ситуации, вот что я делаю:

  1. Создание элемента пользовательского интерфейса в IB.
  2. Добавьте к нему розетку из моего класса контроллеров.
  3. Взломать GDB в awakeFromNib или как угодно.
  4. Из приглашения gdb "p * whatOutlet" ... это покажет вам содержимое структуры C метки NSTextField, которую установил IB.

Глядя на все бесчисленные значения, вы можете получить множество предположений о том, что вы пренебрегаете. Обычно это волшебная комбинация рамок и границ, которая доставляет вас туда, куда вы хотите.

5 голосов
/ 02 октября 2009

Вы можете попробовать использовать nib2objc , чтобы получить все свойства, которые IB устанавливает

2 голосов
/ 02 октября 2009

В частности, вы захотите setBordered:NO и установите стиль рамки на тот стиль, который я забыл. Также setEditable:NO и, необязательно, setSelectable:NO. Этого должно быть достаточно.

1 голос
/ 01 марта 2017

Разобранный AppKit в 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...