Wo soll ich beim programmgesteuerten Erstellen von Ansichten Autolayout-Einschränkungen festlegen?

79

Ich sehe verschiedene Beispiele, in denen Einschränkungen festgelegt werden. Einige setzen sie in viewDidLoad/ loadView(nachdem die Unteransicht hinzugefügt wurde). Andere setzen sie in der Methode updateViewConstraints, die von aufgerufen wird viewDidAppear.

Wenn ich versuche, Einschränkungen updateViewContraintsfestzulegen, kann das Layout nervös werden, z. B. eine leichte Verzögerung, bevor die Ansicht angezeigt wird. Wenn ich diese Methode verwende, sollte ich zuerst vorhandene Einschränkungen beseitigen, z [self.view [removeConstraints:self.view.constraints].

Heisenberg
quelle
1
Ich habe die gleichen Erfahrungen mit updateViewConstraints gemacht, daher habe ich aufgehört, es zu verwenden. Ich konfiguriere Einschränkungen in viewDidLoad oder in der updateConstraints-Methode einer benutzerdefinierten Ansicht. Hoffentlich gibt Ihnen jemand eine endgültige Antwort.
Bilobatum
1
updateViewConstraints: Sie können diese Methode in einer Unterklasse überschreiben, um der Ansicht oder ihren Unteransichten Einschränkungen hinzuzufügen. (aus den Apple-Dokumenten )
Testen

Antworten:

108

Ich habe meine Einschränkungen in viewDidLoad/ eingerichtet loadView(ich ziele auf iOS> = 6 ab). updateViewConstraintsist nützlich, um Werte von Einschränkungen zu ändern. Wenn beispielsweise eine Einschränkung von der Ausrichtung des Bildschirms abhängt (ich weiß, es ist eine schlechte Vorgehensweise), können Sie diese constantin dieser Methode ändern .

Das Hinzufügen von Einschränkungen in viewDidLoadwird während der Sitzung "Einführung in das automatische Layout für iOS und OS X" (WWDC 2012) ab 39:22 angezeigt. Ich denke, es ist eines der Dinge, die während der Vorlesungen gesagt werden, aber nicht in der Dokumentation landen.

UPDATE: Ich habe die Erwähnung des Einrichtens von Einschränkungen in der Ressourcenverwaltung in View Controllern bemerkt :

Wenn Sie Ansichten lieber programmgesteuert erstellen möchten, anstatt ein Storyboard zu verwenden, überschreiben Sie dazu die loadView Methode Ihres Ansichtscontrollers . Ihre Implementierung dieser Methode sollte Folgendes tun:

(...)

3.Wenn Sie das automatische Layout verwenden, weisen Sie jeder der gerade erstellten Ansichten ausreichende Einschränkungen zu, um die Position und Größe Ihrer Ansichten zu steuern . Andernfalls implementieren Sie die Methoden viewWillLayoutSubviewsund viewDidLayoutSubviews, um die Frames der Unteransichten in der Ansichtshierarchie anzupassen. Siehe "Ändern der Größe der Ansichten des View Controllers".

UPDATE 2 : Während der WWDC 2015 Apple eine neue Erklärung gab von updateConstraintsund updateViewConstraintsempfohlene Verwendung:

All dies ist eine Möglichkeit für Ansichten, Änderungen an Einschränkungen rechtzeitig vor dem nächsten Layoutdurchlauf vorzunehmen, die jedoch häufig nicht benötigt werden.

Alle anfänglichen Einstellungen für Einschränkungen sollten idealerweise in Interface Builder erfolgen.

Oder wenn Sie wirklich feststellen, dass Sie Ihre Einschränkungen programmgesteuert zuweisen müssen, ist ein Ort wie viewDidLoad viel besser.

Aktualisierungsbeschränkungen gelten eigentlich nur für Arbeiten, die regelmäßig wiederholt werden müssen.

Außerdem ist es ziemlich einfach, Einschränkungen zu ändern, wenn Sie dies für erforderlich halten. Wenn Sie diese Logik von dem anderen Code, der damit zusammenhängt, trennen und in eine separate Methode verschieben, die zu einem späteren Zeitpunkt ausgeführt wird, ist es viel schwieriger, Ihrem Code zu folgen, sodass Sie ihn schwieriger pflegen können wird es für andere Menschen viel schwieriger sein zu verstehen.

Wann müssten Sie also Aktualisierungsbeschränkungen verwenden?

Nun, es läuft auf Leistung hinaus.

Wenn Sie feststellen, dass es zu langsam ist, nur Ihre Einschränkungen zu ändern, können Aktualisierungsbeschränkungen Ihnen möglicherweise helfen.

Es stellt sich heraus, dass das Ändern einer Einschränkung innerhalb von Aktualisierungsbeschränkungen tatsächlich schneller ist als das Ändern einer Einschränkung zu anderen Zeiten.

Der Grund dafür ist, dass die Engine alle in diesem Durchlauf auftretenden Einschränkungsänderungen als Stapel behandeln kann.

Arek Holko
quelle
2
+1 dafür als beste Antwort. Apple schreibt loadView als den richtigen Ort vor, um anfängliche Einschränkungen festzulegen, und dies erfordert kein zusätzliches BOOL-Flag in der updateConstraints-Methode (was nur hackig erscheint).
Awolf
4
Meiner Meinung nach sollte die Ansicht für die Einschränkungen verantwortlich sein, nicht der Ansichts-Controller. In vielen Fällen weiß der Ansichts-Controller nicht einmal, was alle Elemente in der Ansicht sind (z. B. statische Beschriftungen in einer Tabellenansichtszelle).
Dasdom
1
@dasdom Wie kann die Ansicht ihre Beziehung zu anderen Ansichten steuern? Sie müssen Einschränkungen wie @"|-[button1]-[button2]-|"im ViewController festlegen, oder? Oder gibt es einen anderen Weg?
Joseph
@Casper Meistens habe ich eine UIView-Unterklasse, die die Ansicht des View-Controllers ist. In dieser Ansicht gibt es Unteransichten und die Einschränkungen für die Unteransichten.
Dasdom
3
Warum ist es eine schlechte Praxis, Einschränkungen zu ändern updateViewConstraints?
Testen
33

Ich empfehle, ein BOOL zu erstellen und in -updateConstraintsUIView (oder -updateViewConstraintsfür UIViewController) festzulegen.

-[UIView updateConstraints]: (Apple Docs)

Benutzerdefinierte Ansichten, die selbst Einschränkungen einrichten, sollten diese Methode überschreiben.

Beides -updateConstraintsund -updateViewConstraintskann während der Lebensdauer einer Ansicht mehrmals aufgerufen werden. ( setNeedsUpdateConstraintsWenn Sie eine Ansicht aufrufen, wird dies beispielsweise ausgelöst.) Daher müssen Sie sicherstellen, dass keine doppelten Einschränkungen erstellt und aktiviert werden - entweder mithilfe eines BOOL, um bestimmte Einschränkungen nur einmal einzurichten, oder indem Sie sicherstellen zum Deaktivieren / Entfernen vorhandener Einschränkungen, bevor neue Einschränkungen erstellt und aktiviert werden.

Zum Beispiel:

  - (void)updateConstraints {  // for view controllers, use -updateViewConstraints

         if (!_hasLoadedConstraints) {
              _hasLoadedConstraints = YES;
             // create your constraints
         }
         [super updateConstraints];
    }

Ein Hoch auf @fresidue in den Kommentaren, um darauf hinzuweisen, dass Apples Dokumente den Anruf superals letzten Schritt empfehlen . Wenn Sie aufrufen, superbevor Sie Änderungen an einigen Einschränkungen vornehmen, kann es zu einer Laufzeitausnahme (Absturz) kommen.

BooRanger
quelle
6
Ich bin mir nicht sicher, ob es pragmatisch einen Unterschied macht, aber in der Dokumentation heißt es: "Wichtig: Rufen Sie [super updateConstraints] als letzten Schritt in Ihrer Implementierung auf."
Fresidue
Laut den Dokumenten entfernt das System diese Einschränkung sofort und ruft setNeedsUpdateConstraints auf, wenn Sie zur Laufzeit eine Ansicht ändern und Ihre Einschränkungen ungültig machen. Bevor ein neues Layout ausgeführt wird, ruft das System updateConstraints auf, wo Sie das ungültige Layout anpassen können. Daher würde ich bei dieser Methode wahrscheinlich kein Flag setzen, das das System daran hindern könnte, sie aufzurufen.
smileBot
1
@cocoanutmobile Wenn Sie ein BOOL-Flag verwenden, wie in dieser Antwort vorgeschlagen, dient dies nur dazu, zu verhindern, dass einige Ihrer Einschränkungen mehrmals hinzugefügt werden. Es ist eine sehr gute Sache. Eine andere Alternative besteht darin, einfach einen Verweis auf alle von Ihnen erstellten Einschränkungen zu speichern und dann alle diese zu entfernen (zu deaktivieren), bevor Sie neue erstellen und aktivieren. Dieser Ansatz führt jedoch zu einer schlechteren Leistung, insbesondere wenn Ihre alten und neuen Einschränkungen genau gleich sind.
Smileyborg
2
@cocoanutmobile Beachten Sie auch, dass das BOOL-Flag hier das System nicht daran hindert, anzurufen -updateConstraintsoder so - es würde immer noch aufgerufen und Sie rufen immer noch an [super updateConstraints].
Smileyborg
1
Rufen Sie, wie bei @fresidue erwähnt, superam Ende Ihrer Implementierung von -updateConstraintsoder auf -updateViewConstraints. Weitere Informationen finden Sie in diesem Kommentar .
Smileyborg
4

Dies sollte in ViewDidLoad gemäß dem WWDC-Video von Apple und der Dokumentation erfolgen.

Keine Ahnung, warum Leute updateConstraints empfehlen. Wenn Sie dies in updateConstraints tun, treten Probleme mit NSAutoresizingMaskLayoutConstraint mit automatischer Größenänderung auf, da Ihre Ansichten die automatischen Masken bereits berücksichtigt haben. Sie müssten sie in updateConstraints entfernen, damit sie funktionieren.

UpdateConstraints sollten genau dafür gedacht sein, wenn Sie sie "aktualisieren" müssen, Änderungen usw. gegenüber Ihrer Ersteinrichtung vornehmen müssen.

BluelineCoding
quelle
1
Können Sie die Links für das Video und die Dokumentation hinzufügen, auf die Sie hier verweisen?
Shivam Pokhriyal
2

Tun Sie es in der Ansicht hat Layout-Unteransichten Methode

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
}
Kit
quelle
1

Ich habe diese Lösung, um Einschränkungen zu ändern, bevor diejenigen, die sich im Storyboard befinden, geladen werden. Diese Lösung entfernt alle Verzögerungen, nachdem die Ansicht geladen wurde.

-(void)updateViewConstraints{

    dispatch_async(dispatch_get_main_queue(), ^{

            //Modify here your Constraint -> Activate the new constraint and deactivate the old one

            self.yourContraintA.active = true;
            self.yourContraintB.active= false;
            //ecc..
           });

    [super updateViewConstraints]; // This must be the last thing that you do here -> if! ->Crash!
}
Davide
quelle
1

Sie können sie auch in viewWillLayoutSubviews festlegen :

 override func viewWillLayoutSubviews() {

    if(!wasViewLoaded){
        wasViewLoaded = true

        //update constraint

        //also maybe add a subview            
    }
}
Andre Simon
quelle
0

Das hat bei mir funktioniert:

Swift 4.2

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

// Modify your constraints in here

  ...

}

Obwohl ich ehrlich gesagt nicht sicher bin, ob es sich lohnt. Das Laden scheint etwas langsamer zu sein als in viewDidLoad (). Ich wollte sie nur aus letzterem herausholen, weil es massiv wird.

Michele Dall'Agata
quelle
0

Das folgende Beispiel soll eine Ansicht an eine andere Klasse übergeben. Erstelle meine Ansicht aus dem Storyboard

Swift 5.0

    override func viewWillAppear(_ animated: Bool) {
        
      super.viewWillAppear(animated) 
        DispatchQueue.main.async {
            self.abcInstance = ABC(frame: self.myView.frame)
          } 
      }

 

Wenn Sie DispatchQueue.main.async verpassen, dauert es einige Zeit, um die Einschränkungen in viewWillAppear zu aktualisieren. Erstellen Sie myView im Storyboard und geben Sie Einschränkungen wie Bildschirmbreite und -höhe an. Versuchen Sie dann, den Rahmen von myView zu drucken. In DispatchQueue.main.async oder in viewDidAppear wird ein genauer Wert angegeben, in viewWillAppear ohne DispatchQueue.main.async jedoch kein genauer Wert.

Shrikant Phadke
quelle