2012-06-28 15 views
17

Meine App besteht aus einer NSScrollView, deren Dokumentansicht eine Anzahl von vertikal gestapelten NSTextViews enthält - die sich jeweils in vertikaler Richtung ändern, wenn Text hinzugefügt wird.Auto-Layout mit expandierenden NSTextViews verwenden

Derzeit wird dies alles in Code verwaltet. Die NSTextViews Größe ändert sich automatisch, aber ich beobachte ihre Größenanpassung mit einem NSViewFrameDidChangeNotification, rekaliere alle ihre Ursprünge, so dass sie sich nicht überschneiden, und ändere ihre Superansicht (die Dokumentenansicht der Bildlaufansicht), so dass sie alle passen und gescrollt werden können.

Dies scheint, als wäre es der perfekte Kandidat für das automatische Layout! Ich setze NSLayoutConstraints zwischen der ersten Textansicht und ihrem Container, der letzten Textansicht und ihrem Container und jeder Textansicht untereinander. Wenn dann eine Textansicht wächst, "drückt" sie automatisch die Ursprünge der darunter liegenden Textansichten nach unten, um die Einschränkungen zu erfüllen, wodurch schließlich die Größe der Dokumentansicht wächst, und alle sind glücklich!

Außer, es scheint, es gibt keine Möglichkeit, eine NSTextView automatisch wachsen zu lassen, wie Text in einem Constraints-basierten Layout hinzugefügt wird? Unter Verwendung der exakt gleichen NSTextView, die automatisch erweitert, wie Text zuvor eingegeben wurde, wenn ich keine Beschränkung für seine Höhe angeben, wird es auf 0 zurückgesetzt und wird nicht angezeigt. Wenn ich eine Integritätsbedingung, selbst eine Ungleichung wie> = 20, festlege, bleibt sie bei dieser Größe hängen und wächst nicht, wenn Text hinzugefügt wird.

Ich vermute, dies hat mit NSTextView Implementierung von -intrinsicContentSize zu tun, die standardmäßig (NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric) zurückgibt.

Also meine Fragen: Wenn ich NSTextView Unterklassen intrinsicContentSize basierend auf dem Layout meines Textes zurückgeben würde, würde mein Auto-Layout dann funktionieren wie erwartet?

Gibt es irgendwelche Hinweise auf die Implementierung intrinsicContentSize für eine vertikale Änderung der Größe NSTextView?

+0

Könnten Sie einige Grafik/Screenshot hinzufügen/Wireframe zum besseren Verständnis der Einrichtung bitte? Haben Sie versucht, die Texthöhe zu berechnen und sie in "intrinsicContentSize" zurückzugeben? – JJD

Antwort

10

Ich hatte ein ähnliches Problem mit einem NSTextField, und es stellte sich heraus, dass dies daran lag, dass der View seinen Textinhalt eng entlang der vertikalen Ausrichtung umarmen wollte. Wenn Sie also die Content-Umgehungspriorität auf etwas niedriger als die Prioritäten Ihrer anderen Constraints setzen, kann es funktionieren. Z.B .:

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

So froh, das gefunden zu haben. Ich fand, dass ich dies für vertikal ausgerichtete 'NSTextField's in einem' NSSplitView' tun musste, aber nur wenn sie auswählbar und nicht editierbar waren! So komisch, dass das Editierverhalten Constraints beeinflusst, aber los geht's. Ich änderte die horizontale inhaltliche Priorität für jeden von ihnen von 250 auf 249 und das Problem ging weg. – theory

+0

Unter Swift 4 scheint dies 'setContentHuggingPriority (NSLayoutConstraint.Priority.fittingSizeCompression, für: NSLayoutConstraint.Orientation.vertical)' nicht sicher, was die Bedeutung der -1 war, aber es scheint ohne zu arbeiten. Danke, Marc! –

18

ich auf einer sehr ähnlichen Einrichtung arbeite - einem vertikalen Stapel von Ansichten Textansichten enthält, die ihre Textinhalte passen erweitern und verwenden automatische Layout.

Bisher habe ich hatte NSTextView, Unterklasse, die sauber fühlt sich nicht, aber funktioniert hervorragend in der Praxis:

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

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

Die anfängliche Größe der Textansicht, wenn sie mit addSubview hinzugefügt ist seltsamerweise nicht die intrinsische Größe; Ich habe noch nicht herausgefunden, wie man die erste Ungültigkeitserklärung ausstellt (hooking viewDidMoveToSuperview hilft nicht), aber ich bin sicher, dass ich es schließlich herausfinden werde.

+2

Sehr nützlich! Danke für das Teilen Ihrer Ergebnisse! – jemmons

+1

Hinweis: Ich versuche gerade, die Größe für die animierte Animation zu optimieren, und experimentiere mit der Frage, ob ich die Höhe stattdessen mithilfe von automatischen Layouts steuern kann (oder die tatsächliche Größe ergänzen kann). Es stellt sich heraus, dass Sie eine Einschränkung ändern können, indem Sie ihr eine neue Höhe zuweisen, indem Sie '[constraint setConstant: newHeight]' aufrufen, wobei "constraint" eine einzelne Einschränkung ist, die die Höheneinstellung bereitstellt. Es funktioniert im Moment, steht aber in Konflikt mit der automatischen Größenanpassung von 'NSTextContainer', die zu Flackern führt. –

3

Hier ist, wie ein expandierendes NSTextView mit Auto-Layout zu machen, in Swift 3 enter image description here

  • I verwendet Anchors für Auto-Layout
  • Verwenden textDidChange von NSTextDelegate.NSTextViewDelegate entspricht NSTextDelegate
  • Die Idee ist, dass textViewedges Einschränkungen hat, was bedeutet, wann immer seine intrinsicContentSize ändert, ist es seine Eltern erweitern, die 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() 
        } 
    } 
    
    ist
Verwandte Themen