web-dev-qa-db-ja.com

NSTextFieldの「ラベル」を作成するためのサンプルコードは?

私のデスクトップMac OS Xアプリでは、Interface Builderで作成された一般的なラベルと同じ動作とプロパティを持つNSTextFieldの「ラベル」をプログラムで作成したいと思います。

私は通常IBを使用しますが、この場合はmustをプログラムで実行します。

私が試してみると、IB View Libraryパレットからドラッグされた「Label」と同じlabel-y動作をプログラムで生成するメソッド呼び出しの組み合わせを見つけることができないようです。

誰でもプログラムでこれを行う方法のサンプルコードを提供または指摘できますか? THX。

51

ラベルは、実際には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以降(Sierra)、3つの新しいNSTextFieldコンストラクターがあります。

  • NSTextField(labelWithString:)。ヘッダーファイルのコメントには、「デフォルトのシステムフォントでテキストを表示する、ラッピング、編集、選択ができないテキストフィールドが作成されます。」

  • NSTextField(wrappingLabelWithString:)。ヘッダーファイルのコメントには、「デフォルトのシステムフォントでテキストを表示する、ラップ可能で編集不可能な選択可能なテキストフィールドを作成する」と書かれています。

  • NSTextField(labelWithAttributedString:)。ヘッダーファイルのコメントには、「属性付きテキストを表示する編集不可、選択不可のテキストフィールドを作成します。このフィールドの改行モードは、属性付き文字列のNSParagraphStyle属性によって決定されます。

プレーン(属性のない文字列)を使用するものをテストし、ストーリーボードまたはxibで作成されたテキストフィールドに似ていますが、正確に同じではないテキストフィールドを作成します。

重要な違いは、両方のコンストラクターがtextBackgroundColor(通常は純粋な白)を背景色としてテキストフィールドを作成し、ストーリーボードのテキストフィールドがcontrolColor(通常約90%の白)を使用することです。

重要なことではありませんが、両方のコンストラクターはNSFont.systemFont(ofSize: 0)(以下の私のコードとは異なるNSFontオブジェクトを生成しますが、基盤となる同じコアテキストフォントをラップします)を呼び出してフォントを設定します。

wrappingLabelWithString:コンストラクターは、フィールドのisSelectabletrueに設定します。 (これはヘッダーファイルに記載されています。)


macOS 10.11以前

4つのNSTextFieldインスタンスを比較しました。1つは「ラベル」をストーリーボードにドラッグして作成され、もう1つは「ラッピングラベル」をストーリーボードにドラッグして作成され、2つはコードです。次に、コード作成ラベルのプロパティをすべてストーリーボード作成ラベルとまったく同じになるまで慎重に変更しました。次の2つの方法が結果です。

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

これを正しく行うには注意が必要です。正確なレプリカのレシピは手元にありませんが、同様の状況に陥っているときは、次のようにします。

  1. IBでUI要素を作成します。
  2. コントローラクラスからアウトレットを追加します。
  3. AwakeFromNibなどでgdbを中断します。
  4. Gdbプロンプトから、「p * whateverOutlet」...これにより、IBが設定したラベルNSTextFieldのC構造体の内容が表示されます。

そこにある無数の値をすべて見ると、設定し忘れているものについて多くの推測を得ることができます。通常は、ベゼルと境界線の設定の魔法の組み合わせになり、目的の場所に移動します。

8
danielpunkass

nib2objc を使用して、IBが設定するすべてのプロパティを取得できます。

5
g-Off

具体的には、setBordered:NO、ベゼルのスタイルを忘れたベゼルのスタイルに設定します。また、setEditable:NO、およびオプションでsetSelectable:NO。それで十分です。

2
Steven Degutis

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