Ich versuche, nach Abschluss der Ausführung zum Ende einer UITableView zu scrollen [self.tableView reloadData]
Ich hatte ursprünglich
[self.tableView reloadData]
NSIndexPath* indexPath = [NSIndexPath indexPathForRow: ([self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1) inSection: ([self.tableView numberOfSections]-1)];
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionBottom animated:YES];
Aber dann habe ich gelesen, dass reloadData asynchron ist, so dass das Scrollen seit dem nicht mehr stattfindet self.tableView
, [self.tableView numberOfSections]
und [self.tableView numberOfRowsinSection
sind alle 0.
Vielen Dank!
Was seltsam ist, dass ich benutze:
[self.tableView reloadData];
NSLog(@"Number of Sections %d", [self.tableView numberOfSections]);
NSLog(@"Number of Rows %d", [self.tableView numberOfRowsInSection:([self.tableView numberOfSections]-1)]-1);
In der Konsole wird Sections = 1, Row = -1 zurückgegeben.
Wenn ich genau die gleichen NSLogs in mache, erhalte cellForRowAtIndexPath
ich Sections = 1 und Row = 8; (8 ist richtig)
Antworten:
Das Neuladen erfolgt während des nächsten Layoutdurchlaufs, der normalerweise erfolgt, wenn Sie die Steuerung an die Ausführungsschleife zurückgeben (z. B. nach Ihrer Tastenaktion oder was auch immer zurückkehrt).
Eine Möglichkeit, nach dem erneuten Laden der Tabellenansicht etwas auszuführen, besteht darin, die Tabellenansicht zu zwingen, das Layout sofort auszuführen:
Eine andere Möglichkeit besteht darin, den Code nach dem Layout so zu planen, dass er später ausgeführt wird
dispatch_async
.AKTUALISIEREN
Bei weiteren Untersuchungen stelle ich fest, dass die Tabellenansicht vor der Rückkehr von
tableView:numberOfSections:
undtableView:numberOfRowsInSection:
an ihre Datenquelle gesendet wirdreloadData
. Wenn der Delegat implementierttableView:heightForRowAtIndexPath:
, sendet die Tabellenansicht dies auch (für jede Zeile), bevor Sie von zurückkehrenreloadData
.Die Tabellenansicht sendet jedoch nicht
tableView:cellForRowAtIndexPath:
odertableView:headerViewForSection
erst in der Layoutphase. Dies geschieht standardmäßig, wenn Sie die Steuerung an die Ausführungsschleife zurückgeben.Ich finde auch, dass in einem winzigen Testprogramm der Code in Ihrer Frage ordnungsgemäß zum Ende der Tabellenansicht gescrollt wird, ohne dass ich etwas Besonderes tue (wie Senden
layoutIfNeeded
oder Verwendendispatch_async
).quelle
dispatch_async(dispatch_get_main_queue())
ist nicht garantiert, dass die Methode funktioniert. Ich sehe damit ein nicht deterministisches Verhalten, bei dem das System manchmal die layoutSubviews und das Rendern von Zellen vor dem Abschlussblock und manchmal danach abgeschlossen hat. Ich werde unten eine Antwort posten, die für mich funktioniert hat.dispatch_async(dispatch_get_main_queue())
nicht immer zu arbeiten. Hier werden zufällige Ergebnisse angezeigt.NSRunLoop
. Eine Run-Schleife hat verschiedene Phasen, und Sie können einen Rückruf für eine bestimmte Phase planen (mit aCFRunLoopObserver
). UIKit plant das Layout für eine spätere Phase, nachdem Ihr Event-Handler zurückgekehrt ist.Schnell:
Ziel c:
quelle
tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
Methode überschrieb und in meine Überschreibung alles einfügte, was ich benachrichtigen wollte, dass das Neuladen abgeschlossen war.Ab Xcode 8.2.1, iOS 10 und Swift 3
Sie können das Ende
tableView.reloadData()
einfach mithilfe eines CATransaction-Blocks bestimmen:Das Obige funktioniert auch zum Bestimmen des Endes von reloadData () von UICollectionView und reloadAllComponents () von UIPickerView.
quelle
beginUpdates
undendUpdates
Aufrufe.setCompletionBlock
meinennumberOfSections
Shows 2 ... soweit so gut. Doch wennsetCompletionBlock
ich drinnen bintableView.headerView(forSection: 1)
, kehrt es zurücknil
!!! Daher denke ich, dass dieser Block entweder vor dem Neuladen auftritt oder etwas zuvor erfasst oder ich etwas falsch mache. Zu Ihrer Information, ich habe Tylers Antwort ausprobiert und das hat funktioniert! @ FattieEs
dispatch_async(dispatch_get_main_queue())
ist nicht garantiert, dass die oben beschriebene Methode funktioniert . Ich sehe damit ein nicht deterministisches Verhalten, bei dem das System manchmal die layoutSubviews und das Rendern von Zellen vor dem Abschlussblock und manchmal danach abgeschlossen hat.Hier ist eine Lösung, die unter iOS 10 zu 100% für mich funktioniert. Sie erfordert die Fähigkeit, UITableView oder UICollectionView als benutzerdefinierte Unterklasse zu instanziieren. Hier ist die UICollectionView-Lösung, aber für UITableView ist es genau dasselbe:
CustomCollectionView.h:
CustomCollectionView.m:
Anwendungsbeispiel:
Sehen Sie hier für eine Swift - Version dieser Antwort
quelle
layoutSubviews
sollte er so eingestellt werden, dassnil
nachfolgende Aufrufe vonlayoutSubviews
, nicht unbedingt aufgrund eines AufrufsreloadData
, dazu führen, dass der Block ausgeführt wird, da eine starke Referenz gehalten wird, was nicht das gewünschte Verhalten ist.reloadDataCompletionBlock
ein Array von Blöcken zu erstellen und diese bei der Ausführung zu durchlaufen und das Array danach zu leeren.Ich hatte die gleichen Probleme wie Tyler Sheaffer.
Ich habe seine Lösung implementiert in Swift und sie hat meine Probleme gelöst.
Swift 3.0:
Swift 2:
Anwendungsbeispiel:
quelle
if let
indem Sie sagen,reloadDataCompletionBlock?()
die iff nicht nil 💥self.reloadDataCompletionBlock? { completion() }
hätte sein sollenself.reloadDataCompletionBlock?()
Und eine
UICollectionView
Version, basierend auf der Antwort von kolaworld:https://stackoverflow.com/a/43162226/1452758
Muss getestet werden. Funktioniert bisher unter iOS 9.2, Xcode 9.2 Beta 2, mit dem Scrollen einer CollectionView zu einem Index als Abschluss.
Verwendung:
quelle
Es scheint, dass die Leute diese Frage und die Antworten immer noch lesen. B / c davon bearbeite ich meine Antwort, um das Wort Synchron zu entfernen für dieses wirklich irrelevant ist.
When [tableView reloadData]
gibt zurück, die internen Datenstrukturen hinter der tableView wurden aktualisiert. Wenn die Methode abgeschlossen ist, können Sie daher sicher nach unten scrollen. Ich habe dies in meiner eigenen App überprüft. Die allgemein akzeptierte Antwort von @ rob-mayoff ist zwar auch in der Terminologie verwirrend, bestätigt dies jedoch in seinem letzten Update.Wenn dein
tableView
nicht nach unten scrollen, liegt möglicherweise ein Problem mit einem anderen Code vor, den Sie nicht veröffentlicht haben. Vielleicht ändern Sie Daten, nachdem der Bildlauf abgeschlossen ist, und laden dann nicht neu und / oder scrollen dann nach unten?Fügen Sie wie folgt eine Protokollierung hinzu, um zu überprüfen, ob die Tabellendaten danach korrekt sind
reloadData
. Ich habe den folgenden Code in einer Beispiel-App und es funktioniert perfekt.quelle
reloadData
ist nicht synchron. Früher war es - siehe diese Antwort: stackoverflow.com/a/16071589/193896reloadData
Rückgabe korrekt .reloadData
. Verwenden Sie meinen Testfall inviewWillAppear
Akzeptieren für diescrollToRowAtIndexPath:
Zeile b / c, die bedeutungslos ist, wenn dietableView
nicht angezeigt wird. Sie werden sehen, dassreloadData
die in dertableView
Instanz zwischengespeicherten Daten aktualisiert wurden und dass diesreloadData
synchron ist. Wenn Sie auf anderetableView
Delegatenmethoden verweisen, die beimtableView
Layout des Layouts aufgerufen werden , werden diese nicht aufgerufen, wenn dastableView
nicht angezeigt wird. Wenn ich Ihr Szenario falsch verstehe, erklären Sie es bitte.Ich benutze diesen Trick, ziemlich sicher, dass ich ihn bereits in einem Duplikat dieser Frage gepostet habe:
quelle
Eigentlich hat dieser mein Problem gelöst:
quelle
Versuchen Sie es so, es wird funktionieren
Ich werde ausführen, wenn die Tabelle vollständig geladen ist
Eine andere Lösung ist, dass Sie UITableView unterordnen können
quelle
Am Ende habe ich eine Variation von Shawns Lösung verwendet:
Erstellen Sie eine benutzerdefinierte UITableView-Klasse mit einem Delegaten:
Dann benutze ich in meinem Code
Stellen Sie außerdem sicher, dass Sie Ihre Tabellenansicht im Interface Builder auf CustomTableView setzen:
quelle
In Swift 3.0 + können wir eine Erweiterung für erstellen
UITableView
mit einemescaped Closure
wie unten:Und verwenden Sie es wie unten, wo immer Sie wollen:
hoffe, das wird jemandem helfen. Prost!
quelle
Einzelheiten
Lösung
Verwendung
Vollständige Probe
Ergebnisse
quelle
Nur um einen anderen Ansatz anzubieten, basierend auf der Idee, dass die Fertigstellung die 'letzte sichtbare' Zelle ist, an die gesendet werden soll
cellForRow
.Ein mögliches Problem ist: Wenn
reloadData()
der Vorgang vor demlastIndexPathToDisplay
Festlegen abgeschlossen wurde, wird die 'letzte sichtbare' Zelle angezeigt, bevor sie festgelegtlastIndexPathToDisplay
wurde, und der Abschluss wird nicht aufgerufen (und befindet sich im Status 'Warten'):Wenn wir umkehren, könnte die Fertigstellung durch vorheriges Scrollen ausgelöst werden
reloadData()
.quelle
Versuche dies:
Die tableView-Farbe wird erst nach Abschluss der
reloadData()
Funktion von schwarz nach grün geändert .quelle
Sie können die Funktion performBatchUpdates von uitableview verwenden
So können Sie erreichen
quelle
Erstellen einer wiederverwendbaren Erweiterung von CATransaction:
Erstellen Sie nun eine Erweiterung von UITableView, die die Erweiterungsmethode von CATransaction verwendet:
Verwendung:
quelle
Sie können es verwenden, um nach dem erneuten Laden von Daten etwas zu tun:
quelle
Versuchen Sie, Verzögerungen einzustellen:
quelle