So erkennen Sie das Ende des Ladens von UITableView

141

Ich möchte den Versatz der Tabelle ändern, wenn das Laden beendet ist und dieser Versatz von der Anzahl der in die Tabelle geladenen Zellen abhängt.

Ist es im SDK überhaupt zu wissen, wann das Laden einer geeigneten Ansicht abgeschlossen ist? Ich sehe weder auf Delegaten- noch auf Datenquellenprotokollen etwas.

Ich kann die Anzahl der Datenquellen nicht verwenden, da nur die sichtbaren Zellen geladen werden.

emenegro
quelle
Versuchen Sie eine Kombination aus Datenquellenanzahl und 'indexPathsForVisibleRows'
Swapnil Luktuke
1
Wiedereröffnet basierend auf weiteren Informationen im Flag: "Es ist kein Duplikat. Es fragt nach dem Laden sichtbarer Zellen, nicht nach dem Ende der Datenabfrage. Siehe Aktualisierung der akzeptierten Antwort"
Kev
Diese Lösung funktioniert gut für mich. Sie überprüfen es stackoverflow.com/questions/1483581/…
Khaled Annajar

Antworten:

224

Verbessere auf @RichX Antwort: lastRowkann beides sein [tableView numberOfRowsInSection: 0] - 1oder ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row. Der Code lautet also:

-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if([indexPath row] == ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row){
        //end of loading
        //for example [activityIndicator stopAnimating];
    }
}

UPDATE: Nun, @ htafoyas Kommentar ist richtig. Wenn Sie möchten, dass dieser Code das Ende des Ladens aller Daten aus der Quelle erkennt, wird dies nicht der Fall sein, aber das ist nicht die ursprüngliche Frage. Dieser Code dient zum Erkennen, wann alle Zellen angezeigt werden, die sichtbar sein sollen. willDisplayCell:Wird hier für eine flüssigere Benutzeroberfläche verwendet (einzelne Zellen werden normalerweise nach dem willDisplay:Anruf schnell angezeigt ). Sie können es auch mit versuchen tableView:didEndDisplayingCell:.

Folex
quelle
Viel besser, um zu wissen, wann alle sichtbaren Zellen geladen werden.
Eric
14
Dies wird jedoch immer dann aufgerufen, wenn der Benutzer einen Bildlauf durchführt, um weitere Zellen anzuzeigen.
Htafoya
Das Problem dabei ist, dass eine Fußzeilenansicht nicht berücksichtigt wird. Könnte für die meisten kein Problem sein, aber wenn ja, können Sie die gleiche Idee anwenden, aber in der viewForFooterInSectionMethode.
Kyle Clegg
1
@KyleClegg patric.schenke hat in einem Kommentar zu Ihrer Antwort einen der Gründe genannt. Was ist auch, wenn die Fußzeile am Ende des Ladevorgangs der Zelle nicht sichtbar ist?
Folex
27
tableView: didEndDisplayingCell: wird tatsächlich aufgerufen, wenn eine Zelle aus der Ansicht entfernt wird, nicht, wenn das Rendern in der Ansicht abgeschlossen ist, sodass dies nicht funktioniert. Kein guter Methodenname. Ich muss die Dokumente lesen.
Andrew Raphael
34

Schnelle 3 & 4 & 5 Version:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if let lastVisibleIndexPath = tableView.indexPathsForVisibleRows?.last {
        if indexPath == lastVisibleIndexPath {
            // do here...
        }
    }
}
Daniele Ceglia
quelle
1
Diese Methode wird aufgerufen, wenn Daten zum ersten Mal geladen werden. In meinem Fall gibt es nur 4 sichtbare Zellen. Ich habe 8 Zeilen geladen, aber diese Funktion wurde immer noch aufgerufen. Was ich möchte, ist mehr Daten zu laden, wenn Benutzer nach unten zur letzten Zeile scrollen
Muhammad Nayab
Hallo Muhammad Nayab, vielleicht könnten Sie "if indexPath == lastVisibleIndexPath" durch "if indexPath.row == yourDataSource.count" ersetzen
Daniele Ceglia
1
@ DanieleCeglia Danke, dass du darauf hingewiesen hast. Wenn indexPath == lastVisibleIndexPath && indexPath.row == yourDataSource.count - 1, wird dies für mich funktionieren. Prost!!!
Yogesh Patel
28

Ich benutze immer diese sehr einfache Lösung:

-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if([indexPath row] == lastRow){
        //end of loading
        //for example [activityIndicator stopAnimating];
    }
}
RichX
quelle
Wie erkennt man die letzte Zeile? Das ist das Problem für mich. Kannst du erklären, wie du diesen letzten Wurf in diesen Zustand bringst?
Sameera Chathuranga
6
Letzte Zeile ist die [tableView numberOfRowsInSection: 0] - 1. Sie müssen 0durch den erforderlichen Wert ersetzen . Das ist aber nicht das Problem. Problem ist, dass UITableView nur sichtbar lädt. Allerdings ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).rowlöst das Problem.
Folex
1
Wenn wir nach absoluter Vervollständigung suchen, ist es nicht am besten, - (void) tableView: (UITableView *) tableView didEndDisplayingCell: (UITableViewCell *) Zelle forRowAtIndexPath: (NSIndexPath *) indexPath zu verwenden?
Morcutt
10

Hier ist eine weitere Option, die für mich zu funktionieren scheint. Überprüfen Sie in der viewForFooter-Delegatmethode, ob es sich um den letzten Abschnitt handelt, und fügen Sie dort Ihren Code hinzu. Dieser Ansatz kam mir in den Sinn, nachdem festgestellt wurde, dass willDisplayCell keine Fußzeilen berücksichtigt, wenn Sie diese haben.

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section 
{
  // Perform some final layout updates
  if (section == ([tableView numberOfSections] - 1)) {
    [self tableViewWillFinishLoading:tableView];
  }

  // Return nil, or whatever view you were going to return for the footer
  return nil;
}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section
{
  // Return 0, or the height for your footer view
  return 0.0;
}

- (void)tableViewWillFinishLoading:(UITableView *)tableView
{
  NSLog(@"finished loading");
}

Ich finde, dieser Ansatz funktioniert am besten, wenn Sie die Endbelastung für die gesamte UITableViewund nicht nur für die sichtbaren Zellen ermitteln möchten. Abhängig von Ihren Anforderungen möchten Sie möglicherweise nur die sichtbaren Zellen. In diesem Fall ist die Antwort von folex ein guter Weg.

Kyle Clegg
quelle
Zurück nilvon tableView:viewForFooterInSection:meinem Layout in iOS 7 mit Auto Layout durcheinander.
ma11hew28
Interessant. Was ist, wenn Sie einen Rahmen mit Höhe und Breite 0 festlegen? return CGRectMake(0,0,0,0);
Kyle Clegg
Ich habe das versucht und sogar eingestellt self.tableView.sectionFooterHeight = 0. In beiden Fällen scheint eine Fußzeile mit einer Höhe von etwa 10 eingefügt zu werden. Ich wette, ich könnte dies beheben, indem ich der zurückgegebenen Ansicht eine Einschränkung mit einer Höhe von 0 hinzufüge. Wie auch immer, ich bin gut, weil ich eigentlich herausfinden wollte, wie UITableView in der letzten Zelle gestartet wird, aber dies zuerst gesehen habe.
ma11hew28
1
@MattDiPasquale Wenn Sie viewForFooterInSection implementieren, müssen Sie auch heightForFooterInSection implementieren. Für Abschnitte mit einer Null-Fußzeile muss 0 zurückgegeben werden. Dies steht mittlerweile auch in den offiziellen Dokumenten.
patric.schenke
viewForFooterInSection wird nicht aufgerufen, wenn Sie die heightForFooterInSection auf 0 setzen
Oded Harth
10

Swift 2-Lösung:

// willDisplay function
override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    let lastRowIndex = tableView.numberOfRowsInSection(0)
    if indexPath.row == lastRowIndex - 1 {
        fetchNewDataFromServer()
    }
}

// data fetcher function
func fetchNewDataFromServer() {
    if(!loading && !allDataFetched) {
        // call beginUpdates before multiple rows insert operation
        tableView.beginUpdates()
        // for loop
        // insertRowsAtIndexPaths
        tableView.endUpdates()
    }
}
fatihyildizhan
quelle
9

Private API verwenden:

@objc func tableViewDidFinishReload(_ tableView: UITableView) {
    print(#function)
    cellsAreLoaded = true
}

Öffentliche API verwenden:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // cancel the perform request if there is another section
    [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(tableViewDidLoadRows:) object:tableView];

    // create a perform request to call the didLoadRows method on the next event loop.
    [self performSelector:@selector(tableViewDidLoadRows:) withObject:tableView afterDelay:0];

    return [self.myDataSource numberOfRowsInSection:section];
}

// called after the rows in the last section is loaded
-(void)tableViewDidLoadRows:(UITableView*)tableView{
    self.cellsAreLoaded = YES;
}

Ein mögliches besseres Design besteht darin, die sichtbaren Zellen zu einem Satz hinzuzufügen. Wenn Sie dann überprüfen müssen, ob die Tabelle geladen ist, können Sie stattdessen eine for-Schleife um diesen Satz durchführen, z

var visibleCells = Set<UITableViewCell>()

override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    visibleCells.insert(cell)
}

override func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    visibleCells.remove(cell)
}

// example property you want to show on a cell that you only want to update the cell after the table is loaded. cellForRow also calls configure too for the initial state.
var count = 5 {
    didSet {
        for cell in visibleCells {
            configureCell(cell)
        }
    }
}
malhal
quelle
Viel bessere Lösung als die anderen. Es ist kein erneutes Laden von tableView erforderlich. Es ist sehr einfach und die viewDidLoadRows: werden nicht jedes Mal aufgerufen, wenn die letzte Zelle geladen wird.
Matt Hudson
Dies ist die bisher beste Lösung, da die anderen Lösungen die mehreren Abschnitte nicht berücksichtigen
Justicepenny
Es tut mir leid für den snarky Kommentar, aber wie genau ist diese "Magie"? Aufruf einer anderen Methode vom Delegaten. Ich verstehe es nicht
Lukas Petr
@LukasPetr werfen Sie einen Blick auf den Abbruch und die Nachverzögerung. Diese ermöglichen es, den Methodenaufruf für jeden Abschnitt mit Ausnahme des letzten abzubrechen, wenn die Tabelle vollständig geladen wurde.
Malhal
@ Malhal oh, okay. Es ist ein bisschen schlauer als ich auf den ersten Blick dachte.
Lukas Petr
8

Für die gewählte Antwortversion in Swift 3:

var isLoadingTableView = true

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if tableData.count > 0 && isLoadingTableView {
        if let indexPathsForVisibleRows = tableView.indexPathsForVisibleRows, let lastIndexPath = indexPathsForVisibleRows.last, lastIndexPath.row == indexPath.row {
            isLoadingTableView = false
            //do something after table is done loading
        }
    }
}

Ich brauchte die Variable isLoadingTableView, weil ich sicherstellen wollte, dass die Tabelle vollständig geladen ist, bevor ich eine Standardzellenauswahl vornehme. Wenn Sie dies nicht einschließen, wird Ihr Code jedes Mal, wenn Sie durch die Tabelle scrollen, erneut aufgerufen.

Travis M.
quelle
6

Der beste Ansatz, den ich kenne, ist Erics Antwort unter: Benachrichtigt werden, wenn UITableView die Datenanfrage beendet hat?

Update: Damit es funktioniert, muss ich diese Anrufe tätigen -tableView:cellForRowAtIndexPath:

[tableView beginUpdates];
[tableView endUpdates];
René Retz
quelle
Waht es sich lohnt, ich verwende diese sehr einfache Methode seit Monaten bei allen meinen Projekten und sie funktioniert perfekt.
Eric MORAND
3

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:

  1. Die App empfängt ein Ereignis (Touch, Timer, Block versendet usw.)
  2. Die App behandelt das Ereignis (sie ändert eine Einschränkung, startet eine Animation, ändert den Hintergrund usw.)
  3. Die App berechnet die neue Ansichtshierarchie
  4. 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 reloadDatamehrmals 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, updateLayoutConstraintsusw. in ein UIViewund die entsprechenden Methoden in einer View - Controller - Unterklasse.

Genau das macht eine Tabellenansicht. Es überschreibt layoutSubviewsund basierend auf Ihrer Delegatenimplementierung werden Zellen hinzugefügt oder entfernt. Es wird cellForRowdirekt vor dem Hinzufügen und Auslegen einer neuen Zelle willDisplayaufgerufen. Wenn Sie reloadDatadie 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 willDisplayEreignis des letzten Indexpfads. Dieses Ereignis wird während der Ausführung layoutSubviewsder 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.asyncLö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.

GaétanZ
quelle
1

So geht's in Swift 3:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    if indexPath.row == 0 {
        // perform your logic here, for the first row in the table
    }

    // ....
}
Ondrej Kvasnovsky
quelle
1

Hier ist, wie ich es in Swift 3 mache

let threshold: CGFloat = 76.0 // threshold from bottom of tableView

internal func scrollViewDidScroll(_ scrollView: UIScrollView) {

    let contentOffset = scrollView.contentOffset.y
    let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height;

    if  (!isLoadingMore) &&  (maximumOffset - contentOffset <= threshold) {
        self.loadVideosList()
    }
}
AbdulMomen عبدالمؤمن
quelle
1

Hier ist was ich tun würde.

  1. In Ihrer Basisklasse (kann rootVC BaseVc usw. sein),

    A. Schreiben Sie ein Protokoll, um den Rückruf "DidFinishReloading" zu senden.

    @protocol ReloadComplition <NSObject>
    @required
    - (void)didEndReloading:(UITableView *)tableView;
    @end

    B. Schreiben Sie eine generische Methode, um die Tabellenansicht neu zu laden.

    -(void)reloadTableView:(UITableView *)tableView withOwner:(UIViewController *)aViewController;
  2. Rufen Sie in der Implementierung der Basisklassenmethode reloadData auf, gefolgt von delegateMethod mit Verzögerung.

    -(void)reloadTableView:(UITableView *)tableView withOwner:(UIViewController *)aViewController{
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            [tableView reloadData];
            if(aViewController && [aViewController respondsToSelector:@selector(didEndReloading:)]){
                [aViewController performSelector:@selector(didEndReloading:) withObject:tableView afterDelay:0];
            }
        }];
    }
  3. Bestätigen Sie das Reload-Abschlussprotokoll in allen View-Controllern, in denen Sie den Rückruf benötigen.

    -(void)didEndReloading:(UITableView *)tableView{
        //do your stuff.
    }

Referenz: https://discussions.apple.com/thread/2598339?start=0&tstart=0

Suhas Aithal
quelle
0

@folex Antwort ist richtig.

Es schlägt jedoch fehl, wenn in tableView mehr als ein Abschnitt gleichzeitig angezeigt wird.

-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
   if([indexPath isEqual:((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject])]){
    //end of loading

 }
}
umakanta
quelle
0

In Swift können Sie so etwas tun. Die folgende Bedingung gilt jedes Mal, wenn Sie das Ende der Tabellenansicht erreichen

func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        if indexPath.row+1 == postArray.count {
            println("came to last row")
        }
}
Codetard
quelle
0

Wenn Sie mehrere Abschnitte haben, erhalten Sie folgende Informationen zum Abrufen der letzten Zeile im letzten Abschnitt (Swift 3):

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    if let visibleRows = tableView.indexPathsForVisibleRows, let lastRow = visibleRows.last?.row, let lastSection = visibleRows.map({$0.section}).last {
        if indexPath.row == lastRow && indexPath.section == lastSection {
            // Finished loading visible rows

        }
    }
}
Daniel McLean
quelle
0

Ganz zufällig bin ich auf diese Lösung gestoßen:

tableView.tableFooterView = UIView()
tableViewHeight.constant = tableView.contentSize.height

Sie müssen die Fußzeile einstellen, bevor Sie die contentSize abrufen können, z. B. in viewDidLoad. Übrigens. Durch Einstellen von footeView können Sie "nicht verwendete" Trennzeichen löschen

Kowboj
quelle
-1

Suchen Sie nach der Gesamtzahl der Elemente, die in der Tabelle angezeigt werden, oder nach der Gesamtzahl der aktuell sichtbaren Elemente? So oder so. Ich glaube, dass die 'viewDidLoad'-Methode ausgeführt wird, nachdem alle Datenquellenmethoden aufgerufen wurden. Dies funktioniert jedoch nur beim ersten Laden der Daten (wenn Sie einen einzelnen ViewController zuweisen).

DerekH
quelle
-1

Ich kopiere Andrews Code und erweitere ihn, um den Fall zu berücksichtigen, dass Sie nur 1 Zeile in der Tabelle haben. Es funktioniert soweit für mich!

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
// detect when all visible cells have been loaded and displayed
// NOTE: iOS7 workaround used - see: http://stackoverflow.com/questions/4163579/how-to-detect-the-end-of-loading-of-uitableview?lq=1
NSArray *visibleRows = [tableView indexPathsForVisibleRows];
NSIndexPath *lastVisibleCellIndexPath = [visibleRows lastObject];
BOOL isPreviousCallForPreviousCell = self.previousDisplayedIndexPath.row + 1 == lastVisibleCellIndexPath.row;
BOOL isLastCell = [indexPath isEqual:lastVisibleCellIndexPath];
BOOL isFinishedLoadingTableView = isLastCell && ([tableView numberOfRowsInSection:0] == 1 || isPreviousCallForPreviousCell);

self.previousDisplayedIndexPath = indexPath;

if (isFinishedLoadingTableView) {
    [self hideSpinner];
}
}

HINWEIS: Ich verwende nur einen Abschnitt aus Andrews Code. Denken Sie also daran.

jpage4500
quelle
Willkommen bei SO @ jpage4500. Ich habe Ihre Antwort bearbeitet, um das Zeug über niedrige Wiederholungspunkte und die Fragenbeule zu entfernen. Das Erweitern der Antwort eines anderen Benutzers ist für sich genommen eine vollkommen gültige Antwort. Sie können also die Unordnung verringern, indem Sie das Zeug weglassen. Es ist gut, dass du Andrew Anerkennung gegeben hast, also ist das geblieben.
skrrgwasme
-3

In iOS7.0x ist die Lösung etwas anders. Folgendes habe ich mir ausgedacht.

    - (void)tableView:(UITableView *)tableView 
      willDisplayCell:(UITableViewCell *)cell 
    forRowAtIndexPath:(NSIndexPath *)indexPath
{
    BOOL isFinishedLoadingTableView = [self isFinishedLoadingTableView:tableView  
                                                             indexPath:indexPath];
    if (isFinishedLoadingTableView) {
        NSLog(@"end loading");
    }
}

- (BOOL)isFinishedLoadingTableView:(UITableView *)tableView 
                         indexPath:(NSIndexPath *)indexPath
{
    // The reason we cannot just look for the last row is because 
    // in iOS7.0x the last row is updated before
    // looping through all the visible rows in ascending order 
    // including the last row again. Strange but true.
    NSArray * visibleRows = [tableView indexPathsForVisibleRows];   // did verify sorted ascending via logging
    NSIndexPath *lastVisibleCellIndexPath = [visibleRows lastObject];
    // For tableviews with multiple sections this will be more complicated.
    BOOL isPreviousCallForPreviousCell = 
             self.previousDisplayedIndexPath.row + 1 == lastVisibleCellIndexPath.row;
    BOOL isLastCell = [indexPath isEqual:lastVisibleCellIndexPath];
    BOOL isFinishedLoadingTableView = isLastCell && isPreviousCallForPreviousCell;
    self.previousDisplayedIndexPath = indexPath;
    return isFinishedLoadingTableView;
}
Andrew Raphael
quelle
-3

Ziel c

[self.tableView reloadData];
[self.tableView performBatchUpdates:^{}
                              completion:^(BOOL finished) {
                                  /// table-view finished reload
                              }];

Schnell

self.tableView?.reloadData()
self.tableView?.performBatchUpdates({ () -> Void in

                            }, completion: { (Bool finished) -> Void in
                                /// table-view finished reload
                            })
pkc456
quelle
1
Diese Methode ist meines Wissens nur verfügbar UICollectionView.
Super-o
Ja, du hast recht. Die beginUpdates von UITableView entsprechen den performBatchUpdates: Completion von UICollectionView. stackoverflow.com/a/25128583/988169
pkc456
Geben Sie also eine richtige und gültige Antwort auf die Frage.
G.Abhisek