cocoa osx-lion nstextview autolayout

cocoa - Usando Autolayout con la expansión de NSTextViews



osx-lion (4)

Mi aplicación consta de un NSScrollView cuya vista de documento contiene varios NSTextViews apilados verticalmente, cada uno de los cuales cambia de tamaño en la dirección vertical a medida que se agrega texto.

Actualmente, todo esto está gestionado en código. Las NSTextViews tamaño automáticamente, pero observo que NSViewFrameDidChangeNotification tamaño con un NSViewFrameDidChangeNotification , recalculan todos sus orígenes para que no se superpongan y redimensionan su superview (la vista de documentos de la vista de desplazamiento) para que todos se ajusten y puedan desplazarse.

¡Esto parece como si fuera el candidato perfecto para el autolayout! Establecí NSLayoutConstraints entre la primera vista de texto y su contenedor, la última vista de texto y su contenedor, y cada vista de texto entre sí. Luego, si cualquier vista de texto crece, automáticamente "empuja hacia abajo" los orígenes de las vistas de texto que se encuentran debajo para satisfacer las restricciones, aumentando el tamaño de la vista del documento, ¡y todos están contentos!

¿Excepto que parece que no hay manera de hacer que un NSTextView crezca automáticamente a medida que se agrega texto en un diseño basado en restricciones? Usando exactamente el mismo NSTextView que se expandió automáticamente a medida que se ingresaba el texto antes, si no especifico una restricción para su altura, el valor predeterminado es 0 y no se muestra. Si especifico una restricción, incluso una desigualdad como> = 20, permanece atascada en ese tamaño y no crece a medida que se agrega texto.

Sospecho que esto tiene que ver con la implementación de -intrinsicContentSize de -intrinsicContentSize , que por defecto devuelve (NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric) .

Entonces, mis preguntas: si las subclases NSTextView devuelven un NSTextView de contenido intrinsicContentSize más significativo basado en el diseño de mi texto, ¿mi reproducción automática funcionará como se espera?

¿Algún indicador sobre la implementación de intrinsicContentSize para un redimensionamiento vertical de NSTextView?


Aquí es cómo hacer un NSTextView en expansión utilizando Auto Layout, en Swift 3

  • Anchors para Auto Disposición
  • Utilice textDidChange de NSTextDelegate . NSTextViewDelegate ajusta a NSTextDelegate
  • La idea es que textView tiene restricciones de edges , lo que significa que cada vez que cambie su scrollView intrinsicContentSize , expandirá su padre, que es scrollView

    import Cocoa import Anchors class TextView: NSTextView { override var intrinsicContentSize: NSSize { guard let manager = textContainer?.layoutManager else { return .zero } manager.ensureLayout(for: textContainer!) return manager.usedRect(for: textContainer!).size } } class ViewController: NSViewController, NSTextViewDelegate { @IBOutlet var textView: NSTextView! @IBOutlet weak var scrollView: NSScrollView! override func viewDidLoad() { super.viewDidLoad() textView.delegate = self activate( scrollView.anchor.top.constant(100), scrollView.anchor.paddingHorizontally(30) ) activate( textView.anchor.edges ) } // MARK: - NSTextDelegate func textDidChange(_ notification: Notification) { guard let textView = notification.object as? NSTextView else { return } print(textView.intrinsicContentSize) textView.invalidateIntrinsicContentSize() } }


Clase lista para copiar y pegar. Swift 4.2, macOS 10.14

class HuggingTextView: NSTextView, NSTextViewDelegate { //MARK: - Initialization override init(frame: NSRect) { super.init(frame: frame) delegate = self } override init(frame frameRect: NSRect, textContainer container: NSTextContainer?) { super.init(frame: frameRect, textContainer: container) delegate = self } required init?(coder: NSCoder) { super.init(coder: coder) delegate = self } //MARK: - Overriden override var intrinsicContentSize: NSSize { guard let container = textContainer, let manager = container.layoutManager else { return super.intrinsicContentSize } manager.ensureLayout(for: container) return manager.usedRect(for: container).size } //MARK: - NSTextViewDelegate func textDidChange(_ notification: Notification) { invalidateIntrinsicContentSize() } }


Estoy trabajando en una configuración muy similar: una pila vertical de vistas que contienen vistas de texto que se expanden para ajustarse a su contenido de texto y usan la reproducción automática.

Hasta ahora he tenido que subclasificar NSTextView , que no se siente limpio, pero funciona de manera excelente en la práctica:

- (NSSize) intrinsicContentSize { NSTextContainer* textContainer = [self textContainer]; NSLayoutManager* layoutManager = [self layoutManager]; [layoutManager ensureLayoutForTextContainer: textContainer]; return [layoutManager usedRectForTextContainer: textContainer].size; } - (void) didChangeText { [super didChangeText]; [self invalidateIntrinsicContentSize]; }

El tamaño inicial de la vista de texto cuando se agrega con addSubview es, curiosamente, no el tamaño intrínseco; Todavía no he descubierto cómo emitir la primera invalidación (enganchar viewDidMoveToSuperview no ayuda), pero estoy seguro de que lo resolveré con el tiempo.


Tuve un problema similar con un NSTextField, y resultó que se debía a que la vista quería abrazar su contenido de texto firmemente a lo largo de la orientación vertical. Por lo tanto, si establece la prioridad de aceptación de contenido en un valor inferior a las prioridades de sus otras restricciones, puede funcionar. P.ej:

[textView setContentHuggingPriority:NSLayoutPriorityFittingSizeCompression-1.0 forOrientation:NSLayoutConstraintOrientationVertical];

Y en Swift, esto sería:

setContentHuggingPriority(NSLayoutConstraint.Priority.fittingSizeCompression, for:NSLayoutConstraint.Orientation.vertical)