2012-06-28 13 views
17

La mia app è costituita da un NSScrollView la cui vista del documento contiene un numero di impilati verticalmente NSTextViews - ognuno dei quali viene ridimensionato in direzione verticale quando viene aggiunto del testo.Utilizzo di Autolayout con NSTextViews espandibile

Attualmente, questo è tutto gestito in codice. Il NSTextViews ridimensiona automaticamente, ma osservo il loro ridimensionamento con un NSViewFrameDidChangeNotification, ricalcola tutte le loro origini in modo che non si sovrappongano e ridimensionino la loro superview (la vista del documento della vista di scorrimento) in modo che tutte si adattino e possano scorrere.

Questo sembra essere il candidato perfetto per l'autolayout! Ho impostato NSLayoutConstraints tra la prima visualizzazione di testo e il relativo contenitore, l'ultima visualizzazione di testo e il relativo contenitore e ciascuna vista di testo tra loro. Quindi, se una qualsiasi vista di testo cresce, automaticamente "spinge verso il basso" le origini delle viste di testo sottostanti per soddisfare i vincoli, in definitiva aumentando le dimensioni della vista del documento, e tutti sono felici!

Tranne, sembra che non sia possibile far crescere automaticamente un NSTextView come testo in un layout basato su vincoli? Usando lo stesso identico NSTextView che si espandeva automaticamente quando il testo veniva inserito prima, se non si specifica un vincolo per la sua altezza, si imposta automaticamente su 0 e non viene mostrato. Se specifichi un vincolo, anche una disuguaglianza come> = 20, rimane bloccato a quella dimensione e non cresce man mano che il testo viene aggiunto.

Sospetto che questo abbia a che fare con l'implementazione di NSTextView di -intrinsicContentSize, che per impostazione predefinita restituisce (NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric).

Quindi le mie domande: se sottoclassi NSTextView per restituire uno intrinsicContentSize più significativo in base al layout del mio testo, il mio autolayout funzionerà come previsto?

Eventuali puntatori sull'implementazione di intrinsicContentSize per un NSTextView a ridimensionamento verticale?

+0

Potrebbe aggiungere qualche grafica/screenshot/wireframe per facilitare la comprensione della configurazione per favore? Hai provato a calcolare l'altezza del testo e restituirlo in 'intrinsicContentSize'? – JJD

risposta

10

Ho avuto un problema simile con un NSTextField, e si è scoperto che era dovuto alla vista che voleva abbracciare il suo contenuto di testo strettamente lungo l'orientamento verticale. Quindi, se si imposta il contenuto che abbraccia la priorità su qualcosa di inferiore rispetto alle priorità degli altri vincoli, potrebbe funzionare. Es .:

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

Sono contento di averlo trovato. Ho scoperto che dovevo farlo per 'NSTextField' allineato verticalmente in un' NSSplitView', ma solo quando erano selezionabili e non modificabili! Quindi è strano che il comportamento di editing abbia dei limiti, ma ci sei. Ho cambiato la priorità orizzontale che abbracciava il contenuto per ognuno di essi da 250 a 249 e il problema è andato via. – theory

+0

Sotto Swift 4 questo sembra essere 'setContentHuggingPriority (NSLayoutConstraint.Priority.fittingSizeCompression, per: NSLayoutConstraint.Orientation.vertical)' non so quale sia il significato del -1 ma sembra funzionare senza. Grazie, Marc! –

18

Sto lavorando su una configurazione molto simile - una pila verticale di punti di vista che contengono viste di testo che si espandono per soddisfare le loro contenuti di testo e utilizzare autolayout.

Finora ho dovuto creare una sottoclasse NSTextView, che non si sente pulito, ma funziona superbamente in pratica:

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

- (void) didChangeText { 
    [super didChangeText]; 
    [self invalidateIntrinsicContentSize]; 
} 

La dimensione iniziale della vista testo quando aggiunto con addSubview è, curiosamente, non il dimensione intrinseca; Non ho ancora capito come emettere la prima invalidazione (l'hooking viewDidMoveToSuperview non aiuta), ma sono sicuro che alla fine lo capirò.

+2

Molto utile! Grazie per aver condiviso le tue scoperte! – jemmons

+1

Nota: attualmente sto cercando di far animare la dimensione in modo uniforme e di sperimentare se posso usare gli autolayout per controllare l'altezza (o integrare la dimensione intrinseca). Risulta che è possibile modificare un vincolo dandogli una nuova altezza chiamando '[constraint setConstant: newHeight]', dove 'constraint' è un singolo vincolo che fornisce l'impostazione dell'altezza. Funziona al momento, ma è in conflitto con il ridimensionamento automatico di 'NSTextContainer', con conseguente sfarfallio. –

3

Ecco come fare una NSTextView espansione utilizzando Auto Layout, a Swift 3 enter image description here

  • ho usato Anchors per Auto Layout
  • Usa textDidChange da NSTextDelegate.NSTextViewDelegate conforme alle NSTextDelegate
  • L'idea è che textView ha edges vincoli, il che significa che ogni volta che i suoi intrinsicContentSize modifiche, si espanderà il suo genitore, che è 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() 
        } 
    }