Um zu wissen, wann eine Tabellenansicht den Inhalt vollständig geladen hat, müssen wir zunächst ein grundlegendes Verständnis dafür haben, wie die Ansichten auf dem Bildschirm angezeigt werden.
Im Lebenszyklus einer App gibt es 4 Schlüsselmomente:
- Die App empfängt ein Ereignis (Touch, Timer, Block versendet usw.)
- Die App behandelt das Ereignis (sie ändert eine Einschränkung, startet eine Animation, ändert den Hintergrund usw.)
- Die App berechnet die neue Ansichtshierarchie
- Die App rendert die Ansichtshierarchie und zeigt sie an
Die 2 und 3 Zeiten sind völlig getrennt. Warum ? Aus Leistungsgründen möchten wir nicht jedes Mal, wenn eine Änderung vorgenommen wird, alle Berechnungen des Augenblicks 3 durchführen.
Ich denke, Sie stehen vor einem Fall wie diesem:
tableView.reloadData()
tableView.visibleCells.count // wrong count oO
Was ist hier los?
Wie jede Ansicht lädt eine Tabellenansicht ihren Inhalt träge neu. Wenn Sie reloadData
mehrmals anrufen , entstehen keine Leistungsprobleme. Die Tabellenansicht berechnet nur die Inhaltsgröße basierend auf der Delegatenimplementierung neu und wartet im Moment 3, bis die Zellen geladen sind. Diese Zeit wird als Layout-Pass bezeichnet.
Okay, wie komme ich in den Layout-Pass?
Während des Layoutdurchlaufs berechnet die App alle Frames der Ansichtshierarchie. Sich zu engagieren, können Sie die speziellen Methoden außer Kraft setzen layoutSubviews
, updateLayoutConstraints
usw. in ein UIView
und die entsprechenden Methoden in einer View - Controller - Unterklasse.
Genau das macht eine Tabellenansicht. Es überschreibt layoutSubviews
und basierend auf Ihrer Delegatenimplementierung werden Zellen hinzugefügt oder entfernt. Es wird cellForRow
direkt vor dem Hinzufügen und Auslegen einer neuen Zelle willDisplay
aufgerufen. Wenn Sie reloadData
die Tabellenansicht aufgerufen oder nur der Hierarchie hinzugefügt haben, fügt die Tabellenansicht so viele Zellen hinzu, wie erforderlich sind, um den Rahmen in diesem Schlüsselmoment zu füllen.
Okay, aber jetzt wissen Sie, wann eine Tabellenansicht ihren Inhalt neu geladen hat.
Wir können jetzt diese Frage umformulieren: Woher wissen, wann eine Tabellenansicht ihre Unteransichten fertig angelegt hat?
• Der einfachste Weg ist, in das Layout der Tabellenansicht zu gelangen:
class MyTableView: UITableView {
func layoutSubviews() {
super.layoutSubviews()
// the displayed cells are loaded
}
}
Beachten Sie, dass diese Methode im Lebenszyklus der Tabellenansicht häufig aufgerufen wird. Aufgrund des Bildlaufs und des Warteschlangenverhaltens der Tabellenansicht werden Zellen häufig geändert, entfernt und hinzugefügt. Aber es funktioniert direkt nach dem super.layoutSubviews()
Laden der Zellen. Diese Lösung entspricht dem Warten auf das willDisplay
Ereignis des letzten Indexpfads. Dieses Ereignis wird während der Ausführung layoutSubviews
der Tabellenansicht für jede hinzugefügte Zelle aufgerufen .
• Eine andere Möglichkeit besteht darin, aufgerufen zu werden, wenn die App einen Layoutdurchlauf abgeschlossen hat.
Wie in der Dokumentation beschrieben , können Sie eine der folgenden Optionen verwenden UIView.animate(withDuration:completion)
:
tableView.reloadData()
UIView.animate(withDuration: 0) {
// layout done
}
Diese Lösung funktioniert, aber der Bildschirm wird zwischen dem Zeitpunkt des Layouts und dem Zeitpunkt, zu dem der Block aufgerufen wird, einmal aktualisiert. Dies entspricht der DispatchMain.async
Lösung, ist jedoch angegeben.
• Alternativ würde ich es vorziehen, das Layout der Tabellenansicht zu erzwingen
Es gibt eine spezielle Methode, um jede Ansicht zu zwingen, ihre Unteransichtsrahmen sofort zu berechnen layoutIfNeeded
:
tableView.reloadData()
table.layoutIfNeeded()
// layout done
Seien Sie jedoch vorsichtig, da dadurch die vom System verwendete verzögerte Belastung beseitigt wird. Das wiederholte Aufrufen dieser Methoden kann zu Leistungsproblemen führen. Stellen Sie sicher, dass sie nicht aufgerufen werden, bevor der Rahmen der Tabellenansicht berechnet wird. Andernfalls wird die Tabellenansicht erneut geladen und Sie werden nicht benachrichtigt.
Ich denke, es gibt keine perfekte Lösung. Unterklassen können zu Problemen führen. Ein Layout-Durchgang beginnt von oben nach unten, sodass es nicht einfach ist, benachrichtigt zu werden, wenn das gesamte Layout fertig ist. Und layoutIfNeeded()
könnte Leistungsprobleme usw. verursachen. Wenn Sie diese Optionen kennen, sollten Sie in der Lage sein, eine Alternative zu finden, die Ihren Anforderungen entspricht.