Aby wiedzieć, kiedy widok tabeli kończy ładowanie zawartości, najpierw musimy mieć podstawową wiedzę o tym, jak widoki są wyświetlane na ekranie.
W cyklu życia aplikacji istnieją 4 kluczowe momenty:
- Aplikacja odbiera zdarzenie (dotyk, licznik czasu, wysłanie bloku itp.)
- Aplikacja obsługuje zdarzenie (modyfikuje ograniczenie, uruchamia animację, zmienia tło itp.)
- Aplikacja oblicza nową hierarchię widoków
- Aplikacja renderuje hierarchię widoków i wyświetla ją
Czasy 2 i 3 są całkowicie oddzielone. Czemu ? Ze względu na wydajność nie chcemy wykonywać wszystkich obliczeń w momencie 3 za każdym razem, gdy wprowadzana jest modyfikacja.
Myślę więc, że masz do czynienia z takim przypadkiem:
tableView.reloadData()
tableView.visibleCells.count // wrong count oO
Co tu jest nie tak?
Jak każdy widok, widok tabeli leniwie ponownie ładuje zawartość. Właściwie, jeśli zadzwonisz reloadData
wiele razy, nie spowoduje to problemów z wydajnością. Widok tabeli tylko ponownie oblicza swój rozmiar zawartości na podstawie swojej implementacji delegata i czeka na moment 3, aby załadować swoje komórki. Ten czas nazywa się przebiegiem układu.
OK, jak dostać się do karty układu?
Podczas przebiegu układu aplikacja oblicza wszystkie klatki hierarchii widoków. Aby wziąć udział, można zastąpić dedykowane metody layoutSubviews
, updateLayoutConstraints
etc w krótkim czasie UIView
oraz równoważne metody w podklasie widok kontrolera.
Dokładnie to robi widok tabeli. Zastępuje layoutSubviews
i na podstawie implementacji delegata dodaje lub usuwa komórki. Wywołuje cellForRow
bezpośrednio przed dodaniem i ułożeniem nowej komórki, willDisplay
zaraz po. Jeśli wywołałeś reloadData
lub właśnie dodałeś widok tabeli do hierarchii, widok tabel dodaje tyle komórek, ile potrzeba, aby wypełnić ramkę w tym kluczowym momencie.
W porządku, ale teraz, jak sprawdzić, kiedy widok tabel zakończył ponowne ładowanie zawartości?
Możemy teraz przeformułować to pytanie: skąd wiedzieć, kiedy widok tabeli skończył układać podglądy?
• Najłatwiej jest wejść do układu widoku tabeli:
class MyTableView: UITableView {
func layoutSubviews() {
super.layoutSubviews()
// the displayed cells are loaded
}
}
Zwróć uwagę, że ta metoda jest wywoływana wiele razy w cyklu życia widoku tabeli. Ze względu na przewijanie i usuwanie z kolejki widoku tabeli komórki są często modyfikowane, usuwane i dodawane. Ale to działa zaraz po super.layoutSubviews()
załadowaniu komórek. To rozwiązanie jest równoważne oczekiwaniu na willDisplay
zdarzenie ostatniej ścieżki indeksu. To zdarzenie jest wywoływane podczas wykonywania layoutSubviews
widoku tabeli dla każdej dodanej komórki.
• Innym sposobem jest wywołanie, gdy aplikacja zakończy przebieg układu.
Jak opisano w dokumentacji , możesz skorzystać z opcji UIView.animate(withDuration:completion)
:
tableView.reloadData()
UIView.animate(withDuration: 0) {
// layout done
}
To rozwiązanie działa, ale ekran odświeży się jeden raz między utworzeniem układu a wywołaniem bloku. Jest to równoważne DispatchMain.async
rozwiązaniu, ale określone.
• Alternatywnie wolałbym wymusić układ widoku tabeli
Istnieje specjalna metoda wymuszania natychmiastowego obliczenia ramek widoku podrzędnego w dowolnym widoku layoutIfNeeded
:
tableView.reloadData()
table.layoutIfNeeded()
// layout done
Należy jednak zachować ostrożność, ponieważ spowoduje to usunięcie leniwego ładowania używanego przez system. Wielokrotne wywoływanie tych metod może powodować problemy z wydajnością. Upewnij się, że nie zostaną one wywołane przed obliczeniem ramki widoku tabeli, w przeciwnym razie widok tabeli zostanie ponownie załadowany i nie zostaniesz o tym powiadomiony.
Myślę, że nie ma idealnego rozwiązania. Tworzenie podklas może prowadzić do trubli. Przebieg układu zaczyna się od góry i przechodzi do dołu, więc nie jest łatwo otrzymać powiadomienie, gdy cały układ jest gotowy. I layoutIfNeeded()
może powodować problemy z wydajnością itp. Ale znając te opcje, powinieneś być w stanie pomyśleć o jednej alternatywie, która będzie pasować do Twoich potrzeb.