setNeedsLayout vs. setNeedsUpdate Ograniczenia i układIfNeeded vs updateConstraintsIfNeeded


227

Wiem, że łańcuch automatycznego układu składa się zasadniczo z 3 różnych procesów.

  1. aktualizacja ograniczeń
  2. widoki układu (tutaj otrzymujemy obliczenia ramek)
  3. pokaz

To, co nie jest do końca jasne, to wewnętrzna różnica między -setNeedsLayouti -setNeedsUpdateConstraints. Z Apple Docs:

setNeedsLayout

Wywołaj tę metodę w głównym wątku aplikacji, jeśli chcesz dostosować układ widoków podrzędnych widoku. Ta metoda odnotowuje żądanie i natychmiast zwraca. Ponieważ ta metoda nie wymusza natychmiastowej aktualizacji, ale zamiast tego czeka na następny cykl aktualizacji, można jej użyć do unieważnienia układu wielu widoków przed aktualizacją któregokolwiek z tych widoków. To zachowanie pozwala skonsolidować wszystkie aktualizacje układu w jednym cyklu aktualizacji, co zwykle jest lepsze ze względu na wydajność.

setNeedsUpdateConstraints

Gdy właściwość widoku niestandardowego zmienia się w sposób, który wpływałby na ograniczenia, można wywołać tę metodę, aby wskazać, że ograniczenia muszą zostać zaktualizowane w pewnym momencie w przyszłości. System wywoła następnie updateConstraints jako część normalnego przejścia do układu. Aktualizowanie wiązań naraz tuż przed ich użyciem zapewnia, że ​​nie trzeba niepotrzebnie ponownie obliczać wiązań, gdy między zmianami układu wprowadzanych jest wiele zmian w widoku.

Kiedy chcę animować widok po zmodyfikowaniu ograniczenia i animować zmiany, które zwykle wywołuję na przykład:

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

Odkryłem, że jeśli mogę użyć -setNeedsLayoutzamiast -setNeedsUpdateConstraintswszystko działa zgodnie z oczekiwaniami, ale jeśli zmieni -layoutIfNeededsię -updateConstraintsIfNeeded, animacja nie stanie.
Próbowałem wyciągnąć własny wniosek:

  • -updateConstraintsIfNeeded aktualizuje tylko ograniczenia, ale nie wymusza przejścia układu na proces, dlatego oryginalne ramki są nadal zachowywane
  • -setNeedsLayoutwywołuje także -updateContraintsmetodę

Więc kiedy można używać jednego zamiast drugiego? a jeśli chodzi o metody układu, czy muszę je wywoływać w widoku, który ma zmianę ograniczenia, czy w widoku nadrzędnym?


27
Nie rozumiem ludzi, którzy oddają głos ... naprawdę. Więc powinieneś coś z tym zrobić, na przykład podać powód jako obowiązkowy lub są one całkowicie bezcelowe
— Andrea

7
Być może muszą po prostu zdobyć odznakę Krytyczną (pierwsze głosowanie w dół)
— fujianjin6471

1
Gorąco polecam do zobaczenia tutaj . Odpowiedź jest raczej rozwiązaniem prawdziwego problemu. Zobacz także wideo
— Honey

Odpowiedzi:


258

Twoje wnioski są słuszne. Podstawowy schemat to:

  • setNeedsUpdateConstraintszapewnia przyszłe połączenie z updateConstraintsIfNeededpołączeniami updateConstraints.
  • setNeedsLayoutzapewnia przyszłe połączenie z layoutIfNeededpołączeniami layoutSubviews.

Kiedy layoutSubviewsjest wywoływany, również dzwoni updateConstraintsIfNeeded, więc moim zdaniem rzadko jest potrzebne ręczne wywoływanie go. W rzeczywistości nigdy go nie nazwałem, z wyjątkiem debugowania układów.

Aktualizowanie za pomocą ograniczeń setNeedsUpdateConstraintsjest również dość rzadkie, objc.io - należy przeczytać o autoukładach - mówi :

Jeśli później coś się zmieni, co unieważnia jedno z twoich ograniczeń, powinieneś natychmiast je usunąć i wywołać setNeedsUpdateConstraints. W rzeczywistości jest to jedyny przypadek, w którym powinieneś uruchomić wyzwalanie aktualizacji ograniczenia.

Ponadto, z mojego doświadczenia, nigdy nie musiałem unieważniać ograniczeń i nie ustawiać setNeedsLayoutnastępnego wiersza kodu, ponieważ nowe ograniczenia w zasadzie wymagają nowego układu.

Podstawowe zasady to:

  • Jeśli bezpośrednio manipulowałeś ograniczeniami, zadzwoń setNeedsLayout.
  • Jeśli zmieniono pewne warunki (jak offsetu lub czymś), która będzie zmieniać ograniczeń w swojej zastąpionej updateConstraintsmetoda (zalecanym sposobem ograniczenia zmian, btw), zadzwoń setNeedsUpdateConstraints, a większość czasu, setNeedsLayoutpo tym.
  • Jeśli potrzebujesz któregoś z powyższych działań, aby uzyskać natychmiastowy efekt - np. Gdy musisz nauczyć się nowej wysokości ramki po przejściu układu - dodaj ją za pomocą layoutIfNeeded.

Ponadto w twoim kodzie animacji uważam, że nie setNeedsUpdateConstraintsjest to konieczne, ponieważ ograniczenia są aktualizowane ręcznie przed animacją, a animacja rozkłada widok tylko na podstawie różnic między starymi i nowymi.


@coverback, więc objc.io mówi: „Jeśli później coś się zmieni, co unieważnia jedno z twoich ograniczeń, powinieneś natychmiast usunąć ograniczenie i wywołać setNeedsUpdateConstraints. W rzeczywistości jest to jedyny przypadek, w którym powinieneś uruchomić wyzwalanie aktualizacji ograniczenia”. A potem w bloku Animacja mówi, że kiedy usuwam, dodam lub zmieniam constraint.contant muszę wywołać setNeedsLayout. Co za różnica? Czuję się naprawdę głupio :(
— pash3r

3
@ pash3r Różnica w stałej aktualizacji nie kwalifikuje się jako „unieważnienie”. Unieważnienie ma miejsce wtedy, gdy przestaje być w ogóle istotne, np. Musi zostać dołączone do innego widoku lub całkowicie usunięte. Stała po prostu umieszczałaby widok bliżej lub dalej, lub zmieniałaby jego rozmiar, a zatem potrzeba setNeedsLayout.
— coverback

@ okładka gwarantuje, setNeedsLayoutże layoutSubviewszostanie wywołana w następnym cyklu aktualizacji, ale może to nie ma nic wspólnego layoutIfNeeded?
— fujianjin6471

2
@ okładka Jeśli bezpośrednio manipulujesz ograniczeniami, layoutSubviewszostanie wywołany automatycznie, nie musisz dzwonićsetNeedsLayout
— fujianjin6471

Tak, bezpośrednie manipulowanie właściwościami ograniczenia zostanie uruchomione layoutSubviews, więc nie trzeba tego robić ręcznie. Musisz jednak zadzwonić, layoutIfNeededjeśli chcesz, aby zmiany odniosły skutek natychmiast zamiast następnego cyklu układu
— Charlie Martin

89

Odpowiedź przez coverback jest całkiem poprawne. Chciałbym jednak dodać kilka dodatkowych szczegółów.

Poniżej znajduje się schemat typowego cyklu UIView, który wyjaśnia inne zachowania:

Cykl życia UIView

  1. Odkryłem, że jeśli mogę użyć -setNeedsLayoutzamiast -setNeedsUpdateConstraintswszystko działa zgodnie z oczekiwaniami, ale jeśli zmieni -layoutIfNeededsię -updateConstraintsIfNeeded, animacja nie stanie.

updateConstraintszazwyczaj nic nie robi. Po prostu rozwiązuje ograniczenia, ale nie stosuje ich, dopóki nie layoutSubviewszostanie wywołane. Tak więc animacja wymaga połączenia z layoutSubviews.

  1. setNeedsLayout wywołuje również metodę -updateContraints

Nie, to nie jest konieczne. Jeśli twoje ograniczenia nie zostały zmodyfikowane, UIView pominie połączenie z updateConstraints. Musisz jawnie zadzwonić, setNeedsUpdateConstraintaby zmodyfikować ograniczenia w tym procesie.

Aby zadzwonić updateConstraints, wykonaj następujące czynności:

[view setNeedsUpdateConstraints];
[view setNeedsLayout]; 
[view layoutIfNeeded];

Dzięki, to rozwiązało mój problem. Miałem UIWindow bez nadrzędnego UIView, który miał tymczasowe ograniczenia dodawane do niego po wywołaniu LayoutIfNeeded () przed animacją. Dodanie opakowania subview do UIWindow i wywołanie na nim tych trzech metod naprawiło mój problem.
— masterwok

Nie sądzę, aby wywołanie layoutIfNeeded zaraz po ustawieniu setNeedsLayout było poprawne. Ponieważ metody robią to samo, mimo że jedna powoduje natychmiastowe przerysowanie układu, a druga w następnym cyklu aktualizacji.
— fillky
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.