setNeedsLayout vs. setNeedsUpdateConstraints und layoutIfNeeded vs updateConstraintsIfNeeded

227

Ich weiß, dass die Auto-Layout-Kette im Grunde genommen aus 3 verschiedenen Prozessen besteht.

  1. Einschränkungen aktualisieren
  2. Layoutansichten (hier erhalten wir die Berechnung von Frames)
  3. Anzeige

Was mir nicht ganz klar ist, ist der innere Unterschied zwischen -setNeedsLayoutund -setNeedsUpdateConstraints. Aus Apple Docs:

setNeedsLayout

Rufen Sie diese Methode im Hauptthread Ihrer Anwendung auf, wenn Sie das Layout der Unteransichten einer Ansicht anpassen möchten. Diese Methode notiert die Anfrage und kehrt sofort zurück. Da diese Methode keine sofortige Aktualisierung erzwingt, sondern auf den nächsten Aktualisierungszyklus wartet, können Sie damit das Layout mehrerer Ansichten ungültig machen, bevor eine dieser Ansichten aktualisiert wird. Mit diesem Verhalten können Sie alle Layout-Updates in einem Update-Zyklus konsolidieren, was normalerweise die Leistung verbessert.

setNeedsUpdateConstraints

Wenn sich eine Eigenschaft Ihrer benutzerdefinierten Ansicht so ändert, dass sie sich auf Einschränkungen auswirkt, können Sie diese Methode aufrufen, um anzugeben, dass die Einschränkungen zu einem späteren Zeitpunkt aktualisiert werden müssen. Das System ruft dann updateConstraints als Teil seines normalen Layoutdurchlaufs auf. Durch das gleichzeitige Aktualisieren von Einschränkungen, bevor sie benötigt werden, wird sichergestellt, dass Sie Einschränkungen nicht unnötig neu berechnen, wenn zwischen den Layoutdurchläufen mehrere Änderungen an Ihrer Ansicht vorgenommen werden.

Wenn ich eine Ansicht nach dem Ändern einer Einschränkung animieren und die Änderungen animieren möchte, die ich normalerweise aufrufe, zum Beispiel:

[UIView animateWithDuration:1.0f delay:0.0f usingSpringWithDamping:0.5f initialSpringVelocity:1 options:UIViewAnimationOptionCurveEaseInOut animations:^{
        [self.modifConstrView setNeedsUpdateConstraints];
        [self.modifConstrView layoutIfNeeded];
    } completion:NULL];

Ich habe herausgefunden, dass , wenn ich -setNeedsLayoutstatt -setNeedsUpdateConstraintsalles wie erwartet, aber wenn ich ändern -layoutIfNeededmit -updateConstraintsIfNeeded, die Animation wird nicht passieren.
Ich habe versucht, meine eigene Schlussfolgerung zu ziehen:

  • -updateConstraintsIfNeeded aktualisiert nur Einschränkungen, erzwingt jedoch nicht, dass das Layout in den Prozess einfließt, sodass die ursprünglichen Frames weiterhin erhalten bleiben
  • -setNeedsLayoutruft auch -updateContraintsMethode auf

Wann ist es also in Ordnung, eines anstelle des anderen zu verwenden? Muss ich die Layoutmethoden in der Ansicht aufrufen, in der sich eine Einschränkung geändert hat, oder in der übergeordneten Ansicht?

Andrea
quelle
27
Ich verstehe nicht, dass Leute abstimmen ... wirklich. Also sollten Sie etwas dagegen tun, z. B. einen Grund als obligatorisch erfragen oder sie sind völlig sinnlos
Andrea
7
Vielleicht müssen sie nur das Kritiker-Abzeichen (First Down Vote) erhalten
fujianjin6471
1
Ich kann Ihnen nur empfehlen, hier zu sehen . Die Antwort ist eher eine Lösung für ein echtes Problem. Siehe auch dieses Video
Honey

Antworten:

258

Ihre Schlussfolgerungen sind richtig. Das Grundschema ist:

  • setNeedsUpdateConstraintsstellt sicher, dass ein zukünftiger Anruf updateConstraintsIfNeededanruft updateConstraints.
  • setNeedsLayoutstellt sicher, dass ein zukünftiger Anruf layoutIfNeededanruft layoutSubviews.

Wenn layoutSubviewses aufgerufen wird, ruft es auch an updateConstraintsIfNeeded, so dass es meiner Erfahrung nach selten erforderlich ist, es manuell aufzurufen. Tatsächlich habe ich es nie aufgerufen, außer beim Debuggen von Layouts.

Das Aktualisieren von Einschränkungen mithilfe von setNeedsUpdateConstraintsist ebenfalls ziemlich selten. Objc.io - ein Muss für Autolayouts - sagt :

Wenn sich später etwas ändert, das eine Ihrer Einschränkungen ungültig macht, sollten Sie die Einschränkung sofort entfernen und setNeedsUpdateConstraints aufrufen. In der Tat ist dies der einzige Fall, in dem Sie einen Einschränkungsaktualisierungsdurchlauf auslösen müssen.

Außerdem musste ich meiner Erfahrung nach niemals Einschränkungen ungültig machen und die setNeedsLayoutin der nächsten Zeile des Codes nicht festlegen , da neue Einschränkungen so ziemlich ein neues Layout erfordern.

Die Faustregeln sind:

  • Wenn Sie Einschränkungen direkt manipuliert haben, rufen Sie an setNeedsLayout.
  • Wenn Sie einige Bedingungen geändert haben (z. B. Offsets oder SMTH), die die Einschränkungen in Ihrer überschriebenen updateConstraintsMethode ändern würden (eine empfohlene Methode zum Ändern von Einschränkungen, übrigens), rufen Sie die setNeedsUpdateConstraintsmeiste Zeit setNeedsLayoutdanach auf.
  • Wenn Sie eine der oben genannten Aktionen benötigen, um sofortige Wirkung zu erzielen, z. B. wenn Sie nach einem Layoutdurchlauf eine neue Rahmenhöhe erlernen müssen, fügen Sie diese mit einem hinzu layoutIfNeeded.

Ich glaube auch, dass in Ihrem Animationscode dies setNeedsUpdateConstraintsnicht erforderlich ist, da Einschränkungen vor der Animation manuell aktualisiert werden und die Animation die Ansicht nur basierend auf den Unterschieden zwischen den alten und neuen neu auslegt.

Coverback
quelle
@coverback, also sagt objc.io: "Wenn sich später etwas ändert, das eine Ihrer Einschränkungen ungültig macht, sollten Sie die Einschränkung sofort entfernen und setNeedsUpdateConstraints aufrufen. Tatsächlich ist dies der einzige Fall, in dem Sie einen Einschränkungsaktualisierungsdurchlauf auslösen müssen." Und dann heißt es im Animationsblock, dass ich beim Entfernen, Hinzufügen oder Ändern der Einschränkung setNeedsLayout setNeedsLayout aufrufen muss. Was ist der Unterschied? Ich fühle mich wirklich dumm :(
pash3r
3
@ pash3r Unterschied ist Aktualisierung Konstante gilt nicht als "Ungültigmachung". Invalidierung ist, wenn es überhaupt nicht mehr relevant ist, wie an eine andere Ansicht angehängt oder vollständig entfernt werden muss. Konstante würde lediglich eine Ansicht näher oder weiter platzieren oder ihre Größe ändern, daher die Notwendigkeit für setNeedsLayout.
Coverback
@coverback setNeedsLayoutstellt sicher layoutSubviews, dass es im nächsten Aktualisierungszyklus aufgerufen wird, aber vielleicht hat dies nichts damit zu tun layoutIfNeeded?
Fujianjin6471
2
@coverback Wenn Sie Einschränkungen direkt bearbeiten, layoutSubviewswird automatisch aufgerufen, keine Notwendigkeit zu rufensetNeedsLayout
fujianjin6471
Ja, layoutSubviewswenn Sie die Eigenschaften einer Einschränkung direkt bearbeiten , wird dies ausgelöst , sodass Sie sie nicht manuell ausführen müssen. Sie müssen jedoch anrufen, layoutIfNeededwenn die Änderungen sofort wirksam werden sollen, anstatt des nächsten Layoutzyklus
Charlie Martin,
89

Die Antwort per Coverback ist ziemlich richtig. Ich möchte jedoch einige zusätzliche Details hinzufügen.

Unten sehen Sie das Diagramm eines typischen UIView-Zyklus, in dem andere Verhaltensweisen erläutert werden:

UIViews Lebenszyklus

  1. Ich habe herausgefunden, dass , wenn ich -setNeedsLayoutstatt -setNeedsUpdateConstraintsalles wie erwartet, aber wenn ich ändern -layoutIfNeededmit -updateConstraintsIfNeeded, die Animation wird nicht passieren.

updateConstraintsmacht normalerweise nichts. Es werden nur Einschränkungen aufgelöst, die erst angewendet werden, wenn sie layoutSubviewsaufgerufen werden. Für die Animation ist also ein Anruf bei erforderlich layoutSubviews.

  1. setNeedsLayout ruft auch die Methode -updateContalts auf

Nein, das ist nicht nötig. Wenn Ihre Einschränkungen nicht geändert wurden, überspringt UIView den Anruf zu updateConstraints. Sie müssen explizit aufrufen setNeedsUpdateConstraint, um Einschränkungen im Prozess zu ändern.

Um anzurufen updateConstraints, müssen Sie Folgendes tun:

[view setNeedsUpdateConstraints];
[view setNeedsLayout]; 
[view layoutIfNeeded];
Kunal Balani
quelle
Danke, das hat mein Problem gelöst. Ich hatte ein UIWindow ohne übergeordnetes UIView, dem beim Aufrufen von LayoutIfNeeded () vor einer Animation temporäre Einschränkungen hinzugefügt wurden. Das Hinzufügen eines Subview-Wrappers zum UIWindow und das Aufrufen dieser drei Methoden hat mein Problem behoben.
Masterwok
Ich denke nicht, dass der Aufruf von layoutIfNeeded direkt nach dem setNeedsLayout korrekt ist. Weil die Methoden dasselbe tun, obwohl das Layout sofort neu gezeichnet wird und das zweite in einem nächsten Aktualisierungszyklus.
Fillky