Gibt es eine Möglichkeit herauszufinden, wann a UITableView
die Daten von seiner Datenquelle abgefragt hat?
Keine der viewDidLoad
/ viewWillAppear
/ viewDidAppear
Methoden des zugehörigen View Controllers ( UITableViewController
) ist hier von Nutzen, da sie alle zu früh ausgelöst werden. Keiner von ihnen garantiert (völlig verständlich), dass Abfragen an die Datenquelle vorerst abgeschlossen sind (z. B. bis die Ansicht gescrollt wird).
Eine Abhilfe , die ich gefunden habe , ist zu nennen reloadData
in viewDidAppear
, da bei der reloadData
Rückkehr, die Tabellenansicht ist garantiert beendet zu haben , die Datenquelle so viel abfragt , wie es für den Augenblick braucht.
Dies scheint jedoch ziemlich unangenehm zu sein, da ich davon ausgehe, dass die Datenquelle reloadData
beim ersten Laden zweimal (einmal automatisch und einmal aufgrund des Aufrufs) nach denselben Informationen gefragt wird.
Der Grund, warum ich dies überhaupt tun möchte, ist, dass ich die Bildlaufposition von UITableView
- beibehalten möchte, aber bis auf die Pixelebene, nicht nur bis zur nächsten Zeile.
Wenn die Bildlaufposition Wiederherstellung (mit scrollRectToVisible:animated:
), muß ich die Tabellenansicht bereits genügend Daten darin hat, oder aber der scrollRectToVisible:animated:
Methodenaufruf tut nichts (was was ist, wenn Sie den Anruf auf seinem eigenen in einem Platz geschieht , ist viewDidLoad
, viewWillAppear
oder viewDidAppear
).
quelle
Antworten:
Diese Antwort scheint nicht mehr zu funktionieren, da einige Änderungen an der UITableView-Implementierung vorgenommen wurden, seit die Antwort geschrieben wurde. Siehe diesen Kommentar: Benachrichtigt werden, wenn UITableView die Datenanfrage beendet hat?
Ich habe für ein paar Tage mit diesem Problem gespielt und denke , dass Subclassing
UITableView
‚sreloadData
ist der beste Ansatz:reloadData
endet nicht, bevor die Tabelle ihre Daten neu geladen hat. Wenn also die zweiteNSLog
ausgelöst wird, fragt die Tabellenansicht tatsächlich nach Daten.Ich habe eine Unterklasse erstellt
UITableView
, um vorher und nachher Methoden an den Delegaten zu sendenreloadData
. Es wirkt wie ein Zauber.quelle
reloadData
das sofort zurückkehrt und ich sehe "END reloadData", bevor die Zellen tatsächlich neu geladen werden (dh bevor dieUITableViewDataSource
Methoden aufgerufen werden). Mein Experimentieren zeigt das genaue Gegenteil von dem, was Sie sagen. Ich muss falsch verstehen, was Sie sagen wollen.[super reloadData]
funktioniert für mich :dispatch_async(dispatch_get_main_queue(), ^{NSLog(@"reload complete");});
. Es springt im Grunde Frösche die Blöcke, die von der Tabellenansicht in gepostet werdenreloadData
.Ich hatte das gleiche Szenario in meiner App und dachte, ich würde meine Antwort an euch senden, da andere hier erwähnte Antworten für iOS7 und höher nicht funktionieren
Schließlich ist dies das einzige, was für mich geklappt hat.
Schnelles Update:
Also wie das funktioniert.
Grundsätzlich wird der Haupt-Thread beim Neuladen ausgelastet. Wenn wir also einen asynchronen Versand-Thread ausführen, wartet der Block, bis der Haupt-Thread fertig ist. Sobald die Tabellenansicht vollständig geladen wurde, wird der Hauptthread beendet und unser Methodenblock wird versendet
Getestet in iOS7 und iOS8 und es funktioniert super ;)
Update für iOS9: Dies funktioniert gut, auch iOS9. Ich habe ein Beispielprojekt in Github als POC erstellt. https://github.com/ipraba/TableReloadingNotifier
Ich füge hier den Screenshot meines Tests hinzu.
Getestete Umgebung: iOS9 iPhone6 Simulator von Xcode7
quelle
EDIT: Diese Antwort ist eigentlich keine Lösung. Es scheint wahrscheinlich zunächst zu funktionieren, da das Neuladen ziemlich schnell erfolgen kann, aber tatsächlich wird der Abschlussblock nicht unbedingt aufgerufen, nachdem die Daten vollständig neu geladen wurden - da reloadData nicht blockiert. Sie sollten wahrscheinlich nach einer besseren Lösung suchen.
Um die Antwort von @Eric MORAND zu erweitern, fügen wir einen Abschlussblock ein. Wer liebt einen Block nicht?
und...
Verwendung:
quelle
dispatch_async(dispatch_get_main_queue(), ^{completionBlock();});
. Dieser Sprung führt im Grunde zu den Blöcken, die von der Tabellenansicht in gepostet wurdenreloadData
.reloadData fragt nur nach Daten für die sichtbaren Zellen. Um benachrichtigt zu werden, wenn ein bestimmter Teil Ihrer Tabelle geladen wird, schließen Sie bitte die
tableView: willDisplayCell:
Methode an.quelle
Das ist meine Lösung. 100% funktioniert und wird in vielen Projekten verwendet. Es ist eine einfache UITableView-Unterklasse.
Mit einer Ausnahme ähnelt es der Lösung von Josh Brown . Bei der performSelector-Methode ist keine Verzögerung erforderlich. Egal wie lange es
reloadData
dauert.tableViewDidLoadData:
Feuert immer, wenn SietableView
fertig sinddataSource
cellForRowAtIndexPath
.Auch wenn Sie keine Unterklasse möchten,
UITableView
können Sie einfach aufrufen[performSelector:@selector(finishReload) withObject:nil afterDelay:0.0f]
und Ihr Selektor wird direkt nach dem erneuten Laden der Tabelle aufgerufen. Sie sollten jedoch sicherstellen, dass der Selektor nur einmal pro Aufruf aufgerufen wird, umreloadData
:Genießen. :) :)
quelle
performSelector
oder die Ausführung im Hauptthread mitdispatch_asynch
funktionieren unter iOS 9 nicht .Dies ist eine Antwort auf eine etwas andere Frage: Ich musste wissen, wann
UITableView
auch der Anruf beendet warcellForRowAtIndexPath()
. Ich habe eine UnterklasselayoutSubviews()
(danke @Eric MORAND) erstellt und einen Rückruf für Delegierte hinzugefügt:SDTableView.h:
SDTableView.m:
Verwendung:
MyTableViewController.h:
MyTableViewController.m:
HINWEISE: Da dies eine Unterklasse ist, auf
UITableView
die bereits eine Delegate-Eigenschaft verweist, mussMyTableViewController
keine weitere hinzugefügt werden. Der "@dynamic-Delegat" weist den Compiler an, diese Eigenschaft zu verwenden. (Hier ist ein Link diese beschreiben: http://farhadnoorzay.com/2012/01/20/objective-c-how-to-add-delegate-methods-in-a-subclass/ )Die
UITableView
Eigenschaft inMyTableViewController
muss geändert werden, um die neueSDTableView
Klasse zu verwenden. Dies erfolgt im Interface Builder Identity Inspector. Wählen Sie dasUITableView
Innere vonUITableViewController
und setzen Sie die "Benutzerdefinierte Klasse" aufSDTableView
.quelle
Ich hatte etwas Ähnliches gefunden, um eine Benachrichtigung für die Änderung
contentSize
von zu erhaltenTableView
. Ich denke, das sollte auch hier funktionieren, da sich contentSize auch beim Laden von Daten ändert.Versuche dies:
In
viewDidLoad
schreiben,und fügen Sie diese Methode Ihrem viewController hinzu:
Möglicherweise müssen Sie bei der Überprüfung auf Änderungen geringfügige Änderungen vornehmen. Das hatte aber bei mir funktioniert.
Prost! :) :)
quelle
-viewWillAppear
und entfernen Sie sich in der-viewWillDisapear
Methode.Hier ist eine mögliche Lösung, obwohl es ein Hack ist:
Wo Ihre
-scrollTableView
Methode die Tabellenansicht mit scrollt-scrollRectToVisible:animated:
. Und natürlich können Sie die Verzögerung im obigen Code von 0,3 bis zu dem konfigurieren, was für Sie zu funktionieren scheint. Ja, es ist lächerlich hackig, aber es funktioniert für mich auf meinem iPhone 5 und 4S ...quelle
Ich hatte etwas Ähnliches, glaube ich. Ich habe eine BOOL als Instanzvariable hinzugefügt, die mir sagt, ob der Offset wiederhergestellt wurde, und das eincheckt
-viewWillAppear:
. Wenn es nicht wiederhergestellt wurde, stelle ich es in dieser Methode wieder her und setze BOOL, um anzuzeigen, dass ich den Offset wiederhergestellt habe.Es ist eine Art Hack und es kann wahrscheinlich besser gemacht werden, aber das funktioniert im Moment für mich.
quelle
-viewDidLoad
(wo es natürlich passieren sollte), aber das hat nur funktioniert, wenn ich den Offset animiert eingestellt habe. Das Verschieben der Einstellung des Versatzes-viewWillAppear:
funktionierte, aber ich musste ein Flag beibehalten, um es nur einmal zu setzen. Ich nehme an, die Tabellenansicht lädt ihre Daten neu, sobald sie einer Ansicht hinzugefügt wurden-loadView
. Sind Sie sicher, dass Ihre Daten beim Laden der Ansicht verfügbar sind? Oder wird es in einen separaten Thread geladen oder so?Es hört sich so an, als ob Sie den Zelleninhalt aktualisieren möchten, aber ohne die plötzlichen Sprünge, die mit dem Einfügen und Löschen von Zellen einhergehen können.
Es gibt mehrere Artikel dazu. Dies ist einer.
Ich empfehle die Verwendung von setContentOffset: animiert: anstelle von scrollRectToVisible: animiert: für pixelgenaue Einstellungen einer Bildlaufansicht.
quelle
Sie können die folgende Logik ausprobieren:
Und bevor Sie reloadData aufrufen, setzen Sie prevIndexPath auf nil. Mögen:
Ich habe mit NSLogs getestet, und diese Logik scheint in Ordnung zu sein. Sie können nach Bedarf anpassen / verbessern.
quelle
Endlich habe ich meinen Code damit arbeiten lassen -
Es gab nur wenige Dinge, die erledigt werden mussten -
- (UITableViewCell *)MyTableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
"quelle
Sie können die Größe Ihrer Tabellenansicht ändern oder die Inhaltsgröße in dieser Methode festlegen, wenn alle Daten geladen sind:
quelle
Ich führe nur das Wiederholen des geplanten Timers aus und mache ihn nur ungültig, wenn die contentSize der Tabelle größer ist, wenn die tableHeaderView-Höhe (dh die Tabelle enthält Zeileninhalt). Der Code in C # (Monotouch), aber ich hoffe die Idee ist klar:
quelle
Wird nicht
UITableView
layoutSubviews
aufgerufen, bevor die Tabellenansicht den Inhalt anzeigt? Ich habe festgestellt, dass es aufgerufen wird, sobald die Tabellenansicht ihre Daten geladen hat. Vielleicht sollten Sie dies untersuchen.quelle
Seit iOS 6 heißt die
UITableview
Delegatenmethode:wird ausgeführt, sobald Ihre Tabelle erfolgreich neu geladen wurde. Bei dieser Methode können Sie die erforderlichen Anpassungen vornehmen.
quelle
Die beste Lösung, die ich in Swift gefunden habe
quelle
Warum nicht einfach verlängern?
bis zum Ende scrollen:
Nicht mit vielen Daten getestet
quelle