objective-c - objective - xcode dark mode
¿Código de muestra para crear una "etiqueta" NSTextField? (6)
macOS 10.12 y posterior
Comenzando con macOS 10.12 (Sierra), hay tres nuevos constructores de NSTextField
:
NSTextField(labelWithString:)
, que el comentario del archivo de cabecera dice "Crea un campo de texto que no se puede ajustar, no editable, no seleccionable que muestra texto en la fuente predeterminada del sistema".NSTextField(wrappingLabelWithString:)
, que dice el comentario del archivo de cabecera "Crea un campo de texto seleccionable y no modificable que muestra texto en la fuente predeterminada del sistema".NSTextField(labelWithAttributedString:)
, que el comentario del archivo de cabecera dice "Crea un campo de texto no editable, no seleccionable que muestra texto atribuido. El modo de salto de línea de este campo está determinado por el atributo NSParagraphStyle de la cadena atribuida. "
Probé los que toman una cadena simple (cadena no atribuida), y crean campos de texto que son similares a, pero no exactamente lo mismo que, los campos de texto creados en un guión gráfico o xib.
La diferencia importante es que ambos constructores crean un campo de texto con textBackgroundColor
(normalmente blanco puro) como su color de fondo, mientras que el campo de texto del guión gráfico usa controlColor
(normalmente alrededor del 90% de blanco).
Sin importancia, ambos constructores también configuran sus fuentes llamando a NSFont.systemFont(ofSize: 0)
(que produce un objeto NSFont
diferente de mi código a continuación, pero envuelven la misma fuente subyacente de texto principal).
El constructor wrappingLabelWithString:
establece el campo isSelectable
en true
. (Esto está documentado en el archivo de encabezado).
macOS 10.11 y anterior
Comparé cuatro instancias de NSTextField
: una creada al arrastrar una "Etiqueta" a un guión gráfico, otra creada al arrastrar una "Etiqueta de envoltura" a un guión gráfico, y dos en el código. Luego modifiqué cuidadosamente las propiedades de las etiquetas creadas por código hasta que todas sus propiedades fueran exactamente las mismas que las etiquetas creadas en el guión gráfico. Estos dos métodos son el resultado:
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 usa uno de estos métodos, no olvide configurar el marco de su campo, o desactive sus translatesAutoresizingMaskIntoConstraints
y agregue restricciones.
Aquí está el código que utilicé para comparar los diferentes campos de texto, en caso de que quiera verificar:
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: " "))
}
}
}
En mi aplicación Mac OS X de escritorio, me gustaría crear programáticamente una "etiqueta" NSTextField que tenga el mismo comportamiento y las mismas propiedades que una etiqueta típica creada en Interface Builder.
Usualmente uso (y me gusta mucho) IB, pero en este caso debe ser hecho programáticamente.
Por más que intento, parece que no encuentro la combinación de llamadas a métodos que producirán programáticamente el mismo comportamiento de etiqueta como una "Etiqueta" arrastrada desde la paleta de la Biblioteca de vistas IB.
¿Alguien puede proporcionar o señalar algún código de ejemplo de cómo hacer esto programáticamente? Gracias.
Desmontado AppKit en 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
Específicamente, deseará establecer setBordered:NO
, y establecer el estilo de bisel a lo que sea que ese estilo de bisel sea el que olvidé. También setEditable:NO
, y opcionalmente setSelectable:NO
. Eso debería ser suficiente.
Esto puede ser complicado para hacerlo bien. No tengo la receta para una réplica exacta a mano, pero cuando estoy atrapado en una situación similar, esto es lo que hago:
- Crea un elemento UI en IB.
- Agregue una toma de corriente desde mi clase de controlador.
- Rompe gdb en awakeFromNib o lo que sea.
- Desde el indicador gdb, "p * whateverOutlet" ... esto le mostrará los contenidos de C struct de la etiqueta NSTextField que IB configuró.
Al observar todos los valores innumerables, puede obtener muchas suposiciones sobre lo que está olvidando establecer. Por lo general, termina siendo una combinación mágica de ajustes de bisel y borde, que lo lleva a donde quiere estar.
Podría intentar usar nib2objc para obtener todas las propiedades que IB establece
Una etiqueta es en realidad una instancia de NSTextField , una subclase de NSView. Entonces, dado que es un NSView, debe agregarse a otra vista.
Aquí hay un código de trabajo:
- (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];
}