Wydaje mi się, że dość powszechnym paradygmatem jest pokazywanie / ukrywanie UIViews
, najczęściej w UILabels
zależności od logiki biznesowej. Moje pytanie brzmi: jaki jest najlepszy sposób używania AutoLayout do reagowania na ukryte widoki, tak jakby ich ramka była 0x0. Oto przykład dynamicznej listy 1-3 funkcji.
W tej chwili mam górną przestrzeń od przycisku do ostatniej etykiety o wielkości 10 pikseli, która oczywiście nie przesunie się, gdy etykieta jest ukryta. W tej chwili stworzyłem ujście dla tego ograniczenia i zmodyfikowałem stałą w zależności od tego, ile etykiet wyświetlam. Jest to oczywiście trochę hakerskie, ponieważ używam ujemnych wartości stałych, aby naciskać przycisk nad ukrytymi ramkami. Jest również zły, ponieważ nie jest ograniczony do rzeczywistych elementów układu, tylko podstępne obliczenia statyczne oparte na znanych wysokościach / wypełnieniach innych elementów i oczywiście walka z tym, do czego został zbudowany AutoLayout.
Mógłbym oczywiście po prostu utworzyć nowe ograniczenia w zależności od moich dynamicznych etykiet, ale to dużo mikrozarządzania i dużo gadatliwości przy próbie zwinięcia niektórych białych znaków. Czy są lepsze podejścia? Zmieniasz rozmiar ramki 0,0 i pozwalasz, aby AutoLayout działało bez manipulacji ograniczeniami? Całkowicie usunąć widoki?
Szczerze mówiąc, sama modyfikacja stałej z kontekstu widoku ukrytego wymaga jednej linii kodu z prostymi obliczeniami. Odtwarzanie nowych ograniczeń w programie constraintWithItem:attribute:relatedBy:toItem:attribute:multiplier:constant:
wydaje się takie ciężkie.
Edycja lutego 2018 : Zobacz odpowiedź Bena z UIStackView
s
źródło
Odpowiedzi:
UIStackView
jest prawdopodobnie drogą do iOS 9+. Nie tylko obsługuje ukryty widok, ale także usuwa dodatkowe odstępy i marginesy, jeśli jest skonfigurowane poprawnie.źródło
Moje osobiste preferencje dotyczące pokazywania / ukrywania widoków to tworzenie IBOutlet z odpowiednim ograniczeniem szerokości lub wysokości.
Następnie aktualizuję
constant
wartość, aby0
ukryć lub jakąkolwiek wartość powinna być pokazana.Dużą zaletą tej techniki jest to, że zostaną zachowane względne ograniczenia. Na przykład, powiedzmy, że masz widok A i widok B z poziomą przerwą x . Gdy szerokość widoku A
constant
jest ustawiona na,0.f
widok B przesunie się w lewo, aby wypełnić tę przestrzeń.Nie ma potrzeby dodawania ani usuwania ograniczeń, co jest operacją ciężką. Wystarczy zaktualizować ograniczenie
constant
.źródło
Rozwiązanie polegające na użyciu stałej
0
po ukryciu i innej stałej po jej ponownym wyświetleniu jest funkcjonalne, ale nie daje satysfakcji, jeśli treść ma elastyczny rozmiar. Musiałbyś zmierzyć swoją elastyczną zawartość i ustawić stałą wartość wsteczną. Wydaje się to niewłaściwe i powoduje problemy, jeśli zawartość zmienia rozmiar z powodu zdarzeń serwera lub interfejsu użytkownika.Mam lepsze rozwiązanie.
Chodzi o to, aby reguła wysokości 0 miała wysoki priorytet, gdy ukryjemy element, aby nie zajmował miejsca na autoukładanie.
Oto jak to robisz:
1. ustaw szerokość (lub wysokość) na 0 w konstruktorze interfejsów z niskim priorytetem.
Interface Builder nie będzie krzyczeć o konfliktach, ponieważ priorytet jest niski. Przetestuj zachowanie wysokości, ustawiając tymczasowo priorytet na 999 (1000 nie może programowo mutować, więc nie będziemy go używać). Konstruktor interfejsów prawdopodobnie będzie teraz krzyczeć o sprzecznych ograniczeniach. Możesz to naprawić, ustawiając priorytety na powiązanych obiektach na około 900.
2. Dodaj gniazdko, aby móc zmienić priorytet ograniczenia szerokości w kodzie:
3. Dostosuj priorytet, gdy ukrywasz swój element:
źródło
W tym przypadku przyporządkowuję wysokość etykiety Autor do odpowiedniego IBOutlet:
a kiedy ustawiam wysokość ograniczenia na 0,0f, zachowujemy „wypełnienie”, ponieważ pozwala na to wysokość przycisku Odtwórz.
źródło
Jest tu wiele rozwiązań, ale moje podejście jest znowu inne :)
Ustaw dwa zestawy ograniczeń podobne do odpowiedzi Jorge Arimany'ego i TMina:
Wszystkie trzy zaznaczone ograniczenia mają tę samą wartość dla stałej. Ograniczenia oznaczone A1 i A2 mają priorytet ustawiony na 500, podczas gdy ograniczenie oznaczone B ma priorytet ustawiony na 250 (lub
UILayoutProperty.defaultLow
w kodzie).Podłącz ograniczenie B do IBOutlet. Następnie, gdy ukryjesz element, wystarczy ustawić priorytet ograniczenia na wysoki (750):
constraintB.priority = .defaultHigh
Ale kiedy element jest widoczny, ustaw priorytet z powrotem na niski (250):
constraintB.priority = .defaultLow
(Wprawdzie niewielka) przewaga tego podejścia w porównaniu ze zmianą tylko
isActive
dla ograniczenia B polega na tym, że nadal masz ograniczenie robocze, jeśli element przejściowy zostanie usunięty z widoku w inny sposób.źródło
Podklasuj widok i nadpisz
func intrinsicContentSize() -> CGSize
. Po prostu wróć,CGSizeZero
jeśli widok jest ukryty.źródło
invalidateIntrinsicContentSize
. Możesz to zrobić w trybie overriddensetHidden
.Właśnie się dowiedziałem, że aby UILabel nie zajmował miejsca, musisz go ukryć ORAZ ustawić jego tekst na pusty ciąg. (iOS 9)
Znajomość tego faktu / błędu może pomóc niektórym ludziom uprościć ich układ, być może nawet ten z oryginalnego pytania, więc pomyślałem, że opublikuję go.
źródło
Dziwię się, że nie ma bardziej eleganckiego podejścia
UIKit
do tego pożądanego zachowania. Wydaje się, że bardzo często chce się móc to zrobić.Odkąd łączenie ograniczeń
IBOutlets
i ustawianie ich stałych0
wydawało się obrzydliwe (i powodowałoNSLayoutConstraint
ostrzeżenia, gdy twój widok miał podwidoki), zdecydowałem się stworzyć rozszerzenie, które daje proste, stanowe podejście do ukrywania / pokazywania,UIView
które ma ograniczenia Auto LayoutPo prostu ukrywa widok i usuwa zewnętrzne ograniczenia. Kiedy ponownie pokazujesz widok, dodaje on z powrotem ograniczenia. Jedynym zastrzeżeniem jest to, że musisz określić elastyczne ograniczenia pracy awaryjnej dla otaczających widoków.
Edytuj Ta odpowiedź jest skierowana do iOS 8.4 i starszych. W iOS 9 po prostu zastosuj
UIStackView
podejście.źródło
Najlepszą praktyką jest to, że gdy wszystko ma poprawne ograniczenia układu, dodaj wysokość lub z ograniczeniem, w zależności od tego, jak chcesz, aby otaczające widoki przesuwały się i łączyły ograniczenie z
IBOutlet
właściwością.Upewnij się, że Twoje właściwości są
strong
w kodzie wystarczy ustawić stałą na 0 i aktywować ją, aby ukryć zawartość lub dezaktywować ją, aby wyświetlić zawartość. Jest to lepsze niż zepsucie stałej wartości i przywrócenie jej oszczędności. Nie zapomnij zadzwonić
layoutIfNeeded
później.Jeśli zawartość do ukrycia jest zgrupowana, najlepszą praktyką jest umieszczenie wszystkiego w widoku i dodanie ograniczeń do tego widoku
Gdy masz już swoją konfigurację, możesz ją przetestować w IntefaceBuilder, ustawiając ograniczenie na 0, a następnie przywróć pierwotną wartość. Nie zapomnij sprawdzić innych priorytetów ograniczeń, aby po ukryciu nie było żadnego konfliktu. Innym sposobem sprawdzenia tego jest ustawienie go na 0 i ustawienie priorytetu na 0, ale nie należy zapominać o przywróceniu go do najwyższego priorytetu.
źródło
Buduję kategorię, aby łatwo aktualizować ograniczenia:
Odpowiedź tutaj: Ukryj autoukładanie UIView: Jak uzyskać istniejący NSLayoutConstraint, aby zaktualizować ten
źródło
Moja ulubiona metoda jest bardzo podobna do tej, którą sugerował Jorge Arimany.
Wolę tworzyć wiele ograniczeń. Najpierw utwórz ograniczenia, kiedy druga etykieta będzie widoczna. Utwórz wylot dla ograniczenia między przyciskiem a drugą etykietą (jeśli używasz objc, upewnij się, że jest mocny). To ograniczenie określa wysokość między przyciskiem a drugą etykietą, gdy jest ona widoczna.
Następnie utwórz kolejne ograniczenie określające wysokość między przyciskiem a górną etykietą, gdy drugi przycisk jest ukryty. Utwórz wylot dla drugiego ograniczenia i upewnij się, że ten punkt sprzedaży ma mocną wskazówkę. Następnie usuń zaznaczenie
installed
pola wyboru w narzędziu do tworzenia interfejsów i upewnij się, że priorytet pierwszego ograniczenia jest niższy niż priorytet drugiego ograniczenia.Wreszcie, gdy ukryjesz drugą etykietę, przełącz
.isActive
właściwość tych ograniczeń i wywołajsetNeedsDisplay()
I to wszystko, bez magicznych liczb, bez matematyki, jeśli masz wiele ograniczeń dotyczących włączania i wyłączania, możesz nawet użyć kolekcji outletowych, aby uporządkować je według stanu. (AKA zachowaj wszystkie ukryte ograniczenia w jednym OutletCollection i nieukryte w innym i po prostu iteruj po każdej kolekcji, przełączając ich status .isActive).
Wiem, że Ryan Romanchuk powiedział, że nie chce używać wielu ograniczeń, ale wydaje mi się, że to nie jest mikrozarządzanie-y i jest prostsze, że dynamiczne tworzenie widoków i ograniczeń programowo (tego, jak sądzę, chciał uniknąć, jeśli ja dobrze czytam pytanie).
Stworzyłem prosty przykład, mam nadzieję, że się przyda ...
źródło
Podam również swoje rozwiązanie, aby zaoferować różnorodność.) Myślę, że tworzenie wylotu dla szerokości / wysokości każdego elementu plus odstępy jest po prostu śmieszne i wysadza kod, możliwe błędy i liczbę komplikacji.
Moja metoda usuwa wszystkie widoki (w moim przypadku instancje UIImageView), wybiera, które z nich należy dodać, a następnie w pętli dodaje z powrotem każdy i tworzy nowe ograniczenia. To naprawdę proste, proszę postępuj zgodnie z instrukcją. Oto mój szybki i brudny kod, aby to zrobić:
Otrzymuję czysty, spójny układ i odstępy. Mój kod używa Masonry, zdecydowanie polecam: https://github.com/SnapKit/Masonry
źródło
Wypróbuj BoxView , dzięki czemu dynamiczny układ jest zwięzły i czytelny.
W Twoim przypadku jest to:
źródło