Próbuję uzyskać samokierowanie UICollectionViewCells
przy użyciu Auto Layout, ale wydaje mi się, że nie mogę zmusić komórek do dostosowania się do zawartości. Mam problem ze zrozumieniem, w jaki sposób rozmiar komórki jest aktualizowany na podstawie zawartości zawartości contentView komórki.
Oto konfiguracja, którą wypróbowałem:
- Niestandardowy
UICollectionViewCell
zUITextView
treściąView. - Przewijanie
UITextView
jest wyłączone. - Ograniczeniem poziomym contentView jest: „H: | [_textView (320)]”, tj.
UITextView
Jest on przypięty do lewej części komórki o jawnej szerokości 320. - Ograniczenie pionowe contentView to: „V: | -0 - [_ textView]”, tj. Przypięte
UITextView
do górnej części komórki. UITextView
Ma wysokość wiązania zestaw do stałej którejUITextView
raporty będą pasować do tekstu.
Oto, jak to wygląda z tłem komórki ustawionym na czerwony i UITextView
tło ustawione na niebieski:
Tutaj umieściłem projekt, z którym bawiłem się na GitHub .
Odpowiedzi:
Zaktualizowano dla Swift 5
preferredLayoutAttributesFittingAttributes
zmieniono nazwę napreferredLayoutAttributesFitting
i używaj automatycznego dostosowywaniaZaktualizowano dla Swift 4
systemLayoutSizeFittingSize
przemianowany nasystemLayoutSizeFitting
Zaktualizowano dla iOS 9
Po tym, jak moje rozwiązanie GitHub zepsuło się pod iOS 9, w końcu miałem czas na pełne zbadanie problemu. Zaktualizowałem teraz repozytorium, aby zawierało kilka przykładów różnych konfiguracji dla komórek samokierujących. Doszedłem do wniosku, że komórki samoklejające są świetne w teorii, ale w praktyce są nieuporządkowane. Należy zachować ostrożność podczas wybierania komórek samokierujących.
TL; DR
Sprawdź mój projekt GitHub
Komórki samopoziomujące są obsługiwane tylko z układem przepływu, więc upewnij się, że tego używasz.
Istnieją dwie rzeczy, które musisz skonfigurować, aby komórki o własnych rozmiarach działały.
1. Ustaw
estimatedItemSize
naUICollectionViewFlowLayout
Układ przepływu stanie się dynamiczny z natury po ustawieniu
estimatedItemSize
właściwości.2. Dodaj obsługę zmiany rozmiaru w podklasie komórki
Występuje w 2 smakach; Auto-układ lub niestandardowe zastąpienie
preferredLayoutAttributesFittingAttributes
.Twórz i konfiguruj komórki za pomocą automatycznego układu
Nie będę wchodził w szczegóły na ten temat, ponieważ istnieje genialny post SO dotyczący konfigurowania ograniczeń dla komórki. Bądź ostrożny, że Xcode 6 złamał wiele rzeczy w iOS 7, więc jeśli wspierasz iOS 7, musisz zrobić takie rzeczy, jak upewnić się, że maska autoresizingMas jest ustawiona na contentView komórki i że granice contentView są ustawione jako granice komórki, gdy komórka jest załadowana (tj
awakeFromNib
.).Należy pamiętać, że komórka musi być poważniej ograniczona niż komórka widoku tabeli. Na przykład, jeśli chcesz, aby szerokość była dynamiczna, twoja komórka potrzebuje ograniczenia wysokości. Podobnie, jeśli chcesz, aby wysokość była dynamiczna, będziesz potrzebować ograniczenia szerokości do swojej komórki.
Implementuj
preferredLayoutAttributesFittingAttributes
w niestandardowej komórcePo wywołaniu tej funkcji widok został już skonfigurowany z zawartością (tj.
cellForItem
Został wywołany). Zakładając, że ograniczenia zostały odpowiednio ustawione, możesz mieć taką implementację:UWAGA W systemie iOS 9 zachowanie zmieniło się nieco, co może spowodować awarie implementacji, jeśli nie będziesz ostrożny (zobacz więcej tutaj ). Po wdrożeniu
preferredLayoutAttributesFittingAttributes
musisz upewnić się, że zmienisz ramkę atrybutów układu tylko raz. Jeśli tego nie zrobisz, układ wywoła twoją implementację w nieskończoność i ostatecznie ulegnie awarii. Jednym z rozwiązań jest buforowanie obliczonego rozmiaru w komórce i unieważnianie go za każdym razem, gdy ponownie użyjesz komórki lub zmienisz jej zawartość, tak jak zrobiłem to z tąisHeightCalculated
właściwością.Poznaj swój układ
W tym momencie powinieneś mieć „funkcjonujące” dynamiczne komórki w swojej kolekcjiViewView. Nie znalazłem jeszcze gotowego rozwiązania wystarczającego podczas moich testów, więc możesz to skomentować, jeśli tak. Nadal wydaje się, że
UITableView
wygrywa bitwę o dynamiczne zmiany rozmiaru IMHO.Ostrzeżenia
Pamiętaj, że jeśli używasz prototypowych komórek do obliczania oszacowanegoItemSize - to się zepsuje, jeśli twój XIB używa klas wielkości . Powodem tego jest to, że po załadowaniu komórki z XIB zostanie skonfigurowana klasa wielkości
Undefined
. Zostanie to zerwane tylko w systemie iOS 8 i nowszym, ponieważ w systemie iOS 7 klasa wielkości zostanie załadowana na podstawie urządzenia (iPad = zwykły-dowolny, iPhone = kompaktowy-dowolny). Możesz ustawić oszacowaną wartość Bez ładowania XIB lub załadować komórkę z XIB, dodać ją do collectionView (spowoduje to ustawienie funkcji traitCollection), wykonać układ, a następnie usunąć z superwizji. Alternatywnie możesz również sprawić, że twoja komórka zastąpitraitCollection
getter i zwróci odpowiednie cechy. To zależy od Ciebie.Daj mi znać, jeśli coś przeoczyłem, mam nadzieję, że pomogłem i powodzenia w kodowaniu
źródło
estimatedItemSize
prowadzi do awarii - wydaje się, że istnieje duży błąd w sposobie, w jaki układ automatyczny próbuje przetworzyć UICollectionViewCell.W iOS10 jest nowa stała o nazwie
UICollectionViewFlowLayout.automaticSize
(dawniejUICollectionViewFlowLayoutAutomaticSize
), więc zamiast tego:możesz użyć tego:
Ma lepszą wydajność, szczególnie gdy komórki w twoim widoku kolekcji mają stałą szerokość
Dostęp do układu przepływu:
Aktualizacja Swift 5:
źródło
collectionViewLayout
i po prostu sprawdzić, czy jest on typuUICollectionViewFlowLayout
Kilka kluczowych zmian w odpowiedzi Daniela Galasko naprawiło wszystkie moje problemy. Niestety nie mam wystarczającej reputacji, aby komentować bezpośrednio (jeszcze).
W kroku 1, gdy korzystasz z automatycznego układu, po prostu dodaj do komórki pojedynczego rodzica UIView. WSZYSTKO w komórce musi być widokiem rodzica. To odpowiedziało na wszystkie moje problemy. Xcode dodaje to automatycznie dla UITableViewCells, ale nie (ale powinno) dla UICollectionViewCells. Według dokumentów :
Następnie całkowicie pomiń krok 3. To nie jest potrzebne.
źródło
contentView
, Xcode narzeka, że jest w konflikcie z istniejącą własnością. Próbowałem dodać widoki podrzędneself.contentView
i ustawić ograniczenia, a następnie aplikacja ulega awarii.W iOS 10+ jest to bardzo prosty 2-etapowy proces.
Upewnij się, że cała zawartość komórki jest umieszczona w jednym UIView (lub w potomku UIView, takim jak UIStackView, co znacznie upraszcza autoukład). Podobnie jak w przypadku dynamicznego zmieniania rozmiaru UITableViewCells, cała hierarchia widoku musi mieć skonfigurowane ograniczenia, od najbardziej zewnętrznego kontenera do najbardziej wewnętrznego widoku. Obejmuje to ograniczenia między UICollectionViewCell a bezpośrednim widokiem dziecka
Poinstruuj flowlayout UICollectionView, aby automatycznie dobierał rozmiar
źródło
UICollectionViewFlowLayout.automaticSize
że został przemianowany naUICollectionViewFlowLayoutAutomaticSize
.Dodaj flowLayout do viewDidLoad ()
Ponadto ustaw UIView jako mainContainer dla swojej komórki i dodaj do niej wszystkie wymagane widoki.
Zapoznaj się z tym niesamowitym, oszałamiającym samouczkiem, aby uzyskać dodatkowe informacje: UICollectionView z funkcją automatycznego dopasowywania komórek za pomocą funkcji automatycznego układania w iOS 9 i 10
źródło
EDYCJA 19.11.19: W systemie iOS 13 wystarczy użyć UICollectionViewCompositionalLayout z szacowanymi wysokościami. Nie trać czasu na radzenie sobie z tym zepsutym API.
Po dłuższej walce z tym zauważyłem, że zmiana rozmiaru nie działa w UITextViews, jeśli nie wyłączysz przewijania:
źródło
contentView anchor mystery:
W jednym dziwnym przypadku to
nie działałoby Dodano cztery wyraźne kotwice do contentView i zadziałało.
i jak zwykle
w
YourLayout: UICollectionViewFlowLayout
Kto wie? Może komuś pomóc.
Kredyt
https://www.vadimbulavin.com/collection-view-cells-self-sizing/
natknąłem się na końcówkę - nigdy nie widziałem go nigdzie indziej we wszystkich tysiącach artykułów na ten temat.
źródło
Zrobiłem dynamiczną wysokość komórki widoku kolekcji. Oto repozytorium git hub .
I dowiedz się, dlaczego preferowanyLayoutAttributesFittingAttributes jest wywoływany więcej niż jeden raz. W rzeczywistości zostanie wywołany co najmniej 3 razy.
Obraz dziennika konsoli:
1st preferowanyLayoutAttributesFittingAttributes :
Wysokość layoutAttributes.frame.size.height ma aktualny status 57,5 .
2nd preferowany LayoutAttributesFittingAttributes :
Wysokość ramki komórki zmieniła się na 534,5 zgodnie z naszymi oczekiwaniami. Ale widok kolekcji wciąż ma zerową wysokość.
3 preferowanyLayoutAttributesFittingAttributes :
Możesz zobaczyć, że wysokość widoku kolekcji została zmieniona z 0 na 477 .
Zachowanie jest podobne do obsługi przewijania:
Na początku myślałem, że ta metoda wywołuje tylko raz. Więc kodowałem w następujący sposób:
Ta linia:
spowoduje wywołanie nieskończonej pętli systemowej i awarię aplikacji.
Każda zmiana rozmiaru spowoduje, że wszystkie komórki będą miały preferowanyLayoutAttributesFittingAttributes, dopóki pozycje każdej komórki (tj. Ramki) nie będą się już zmieniać.
źródło
Oprócz powyższych odpowiedzi
Po prostu upewnij się, że ustawiłeś właściwość szacowanegoItemSize UICollectionViewFlowLayout na pewien rozmiar i nie implementujesz sizeForItem: atIndexPath metodę delegata.
Otóż to.
źródło
Jeśli zaimplementujesz metodę UICollectionViewDelegateFlowLayout:
- (CGSize)collectionView:(UICollectionView*)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath*)indexPath
Kiedy zadzwonisz
collectionview performBatchUpdates:completion:
, użyjesz wysokościsizeForItemAtIndexPath
zamiastpreferredLayoutAttributesFittingAttributes
.Proces renderowania
performBatchUpdates:completion
przejdzie przez metodę,preferredLayoutAttributesFittingAttributes
ale ignoruje twoje zmiany.źródło
Komukolwiek to może pomóc,
Miałem ten paskudny wypadek, jeśli
estimatedItemSize
został ustawiony. Nawet jeśli zwróciłem 0 wnumberOfItemsInSection
. Dlatego same komórki i ich automatyczny układ nie były przyczyną awarii ... CollectionView po prostu się zawiesił, nawet gdy jest pusty, tylko dlatego, żeestimatedItemSize
został ustawiony na samorozmiar.W moim przypadku zreorganizowałem mój projekt, od kontrolera zawierającego collectionView do collectionViewController i zadziałało.
Domyśl.
źródło
collectionView.collectionViewLayout.invalidateLayout()
pocollectionView.reloadData()
.Dla każdego, kto spróbował wszystkiego bez powodzenia, jest to jedyna rzecz, która sprawiła, że działało to dla mnie. W przypadku etykiet wielowierszowych wewnątrz komórki spróbuj dodać tę magiczną linię:
Więcej informacji: tutaj
Twoje zdrowie!
źródło
Powyższa przykładowa metoda się nie kompiluje. Oto poprawiona wersja (ale niesprawdzona, czy działa).
źródło
Zaktualizuj więcej informacji:
Jeśli używasz
flowLayout.estimatedItemSize
, sugeruj użycie późniejszej wersji iOS 8.3. Przed iOS 8.3 nastąpi awaria[super layoutAttributesForElementsInRect:rect];
. Komunikat o błędzie to*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
Po drugie, w wersji iOS8.x
flowLayout.estimatedItemSize
spowoduje, że różne ustawienia wstawek sekcji nie działały. czyli funkcja:(UIEdgeInsets)collectionView:layout:insetForSectionAtIndex:
.źródło
Rozwiązanie składa się z 4 ważnych kroków:
flowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
contentView
do krawędzi komórki, ponieważcontentView
zapobiega samozaklejeniu, ponieważ nie ma włączonego automatycznego układu.collectionView(:cellForItemAt:)
aby ograniczyć szerokość contentView do szerokości collectionView.Dzieje się to w następujący sposób; będziemy ustawić komórki
maxWidth
zmiennej szerokości CollectionView pod adresemcollectionView(:cellForItemAt:)
iwmaxWidth
„sdidSet
metody będziemy ustawić widthAnchor.constant do maxwidth.Ponieważ chcesz włączyć automatyczne dostosowywanie rozmiaru UITextView, ma on dodatkowy krok do;
4. Oblicz i ustaw wysokośćAnchor.constant w UITextView.
Tak więc, ilekroć ustawiona jest szerokość contentView, dostosowujemy wysokość UITextView wraz
didSet
zmaxWidth
.Inside UICollectionViewCell:
Kroki te zapewnią pożądany rezultat. Oto pełna runnable Gist
Dzięki Vadimowi Bulavinowi za jego jasny i pouczający post na blogu - Kolekcja Wyświetl komórki Samowyrównanie : samouczek krok po kroku
źródło
Próbowałem użyć,
estimatedItemSize
ale było wiele błędów podczas wstawiania i usuwania komórek, jeśliestimatedItemSize
nie były dokładnie równe wysokości komórki. Przestałem ustawiaćestimatedItemSize
i zaimplementowałem dynamiczne komórki za pomocą prototypowej komórki. oto jak to się robi:utwórz ten protokół:
zaimplementuj ten protokół w swoim własnym
UICollectionViewCell
:teraz spraw, aby twój kontroler był zgodny
UICollectionViewDelegateFlowLayout
i zawiera w tym polu:zostanie użyty jako komórka prototypowa do powiązania danych, a następnie określi, w jaki sposób dane wpłynęły na wymiar, który ma być dynamiczny
wreszcie
UICollectionViewDelegateFlowLayout's
collectionView(:layout:sizeForItemAt:)
należy wprowadzić:i to wszystko. Zwracanie wielkości komórki
collectionView(:layout:sizeForItemAt:)
w ten sposób uniemożliwia mi użycieestimatedItemSize
, a wstawianie i usuwanie komórek działa idealnie.źródło