Come sapere quando UITableView ha fatto scorrere verso il basso in iPhone


100

Vorrei sapere quando è stato UITableVieweseguito lo scorrimento verso il basso per caricare e mostrare più contenuto, qualcosa come un delegato o qualcos'altro per far sapere al controller quando la tabella è andata in fondo.

Qualcuno lo sa, per favore aiutami, grazie in anticipo!

Risposte:


18

Il modo migliore è testare un punto nella parte inferiore dello schermo e utilizzare questa chiamata di metodo ogni volta che l'utente scorre ( scrollViewDidScroll):

- (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point

Testare un punto vicino alla parte inferiore dello schermo, quindi utilizzando indexPath restituisce il controllo se tale indexPath è l'ultima riga, quindi se lo è, aggiungi righe.


Sfortunatamente la mia app aggiornerà i dati in tempo reale per le righe esistenti in questa tabella, quindi in questo modo potrebbe ottenere più contenuto quando la tabella non scorre affatto.
Son Nguyen

247

nel delegato tableview fare qualcosa di simile

ObjC:

- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
    CGPoint offset = aScrollView.contentOffset;
    CGRect bounds = aScrollView.bounds;
    CGSize size = aScrollView.contentSize;
    UIEdgeInsets inset = aScrollView.contentInset;
    float y = offset.y + bounds.size.height - inset.bottom;
    float h = size.height;
    // NSLog(@"offset: %f", offset.y);   
    // NSLog(@"content.height: %f", size.height);   
    // NSLog(@"bounds.height: %f", bounds.size.height);   
    // NSLog(@"inset.top: %f", inset.top);   
    // NSLog(@"inset.bottom: %f", inset.bottom);   
    // NSLog(@"pos: %f of %f", y, h);

    float reload_distance = 10;
    if(y > h + reload_distance) {
        NSLog(@"load more rows");
    }
}

Swift:

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let offset = scrollView.contentOffset
    let bounds = scrollView.bounds
    let size = scrollView.contentSize
    let inset = scrollView.contentInset
    let y = offset.y + bounds.size.height - inset.bottom
    let h = size.height
    let reload_distance:CGFloat = 10.0
    if y > (h + reload_distance) {
        print("load more rows")
    }
}

Buona! Basta usare: y> = h + reload_distance, che è davvero una preoccupazione solo quando reload_distance = 0 (ad esempio, per i rimbalzi NO).
Chris Prince,

4
Questo codice in "scrollViewDidScroll" viene eseguito per tutto il tempo in cui l'utente sposta il dito su / giù sullo schermo. È meglio spostarlo sotto "scrollViewDidEndDecelerating". In questo modo verrà eseguito una volta quando l'utente smette di muovere il dito.
Misha

hmm .. questo codice funziona ancora? non ottenere il fondo di UITableView per me. Vorrei conoscere il ragionamento alla base di tutto il codice sopra, quindi forse posso risolvere
FlowUI. SimpleUITesting.com

offset: 237.000000 content.height: 855.000000 bounds.height: 667.000000 inset.top: 64.000000 inset.bottom: 49.000000 pos: 855.000000 di 855.000000 se diventa falso
Abhishek Thapliyal

62

I neoney modificati rispondono un po '.

Questa risposta si rivolge a quelli di voi che desiderano che l'evento venga attivato una sola volta per ogni rilascio del dito .

Adatto quando si caricano più contenuti da alcuni provider di contenuti (servizio Web, dati principali ecc.). Tieni presente che questo approccio non rispetta il tempo di risposta del tuo servizio web.

- (void)scrollViewDidEndDragging:(UIScrollView *)aScrollView
                  willDecelerate:(BOOL)decelerate
{
    CGPoint offset = aScrollView.contentOffset;
    CGRect bounds = aScrollView.bounds;
    CGSize size = aScrollView.contentSize;
    UIEdgeInsets inset = aScrollView.contentInset;
    float y = offset.y + bounds.size.height - inset.bottom;
    float h = size.height;

    float reload_distance = 50;
    if(y > h + reload_distance) {
        NSLog(@"load more rows");
    }
}

Questo non funziona. Ogni volta che l'utente scorre verso il basso, viene chiamato "carica più righe". Ciò accade anche quando l'utente ha già eseguito lo scorrimento verso il basso e sta aspettando che l'API restituisca dati.
Dean

2
Dean, la domanda era sapere quando la visualizzazione tabella scorreva verso il basso. Se stai recuperando dati da un'API o no è irrilevante, non è vero?
Eyeball

Ma non vi è alcun messaggio che mostri caricare di più .. in modo che l'utente possa essere consapevole che ci sono più risultati da mostrare.
iPhone 7

Bella risposta nel mio caso funziona se: float reload_distance = 10; if (y> (h - reload_distance)) {NSLog (@ "carica più righe"); } perché y = 565 e anche h è 565
Abhishek Thapliyal

1
Ho provato sia la risposta di Eyeball che quella di @neoneye. Credimi o no "scrollViewDidScroll" chiamato più volte rispetto a "scrollViewDidEndDragging". Quindi è stato efficiente usare "scrollViewDidEndDragging" poiché ho una chiamata API su di esso.
Vinod Supnekar

39

aggiungi questo metodo nel UITableViewDelegate:

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{   
    CGFloat height = scrollView.frame.size.height;

    CGFloat contentYoffset = scrollView.contentOffset.y;

    CGFloat distanceFromBottom = scrollView.contentSize.height - contentYoffset;

    if(distanceFromBottom < height) 
    {
        NSLog(@"end of the table");
    }
}

3
Mi piace questa soluzione tranne un piccolo dettaglio. if (distanceFromBottom <= height)
Mark McCorkle

Questo mi ha aiutato a risolvere il mio problema, grazie !! una barra delle schede !! era uno stupido tab bar !!
Dmytro

questo funziona come un incantesimo, ed è anche semplice, ma sto affrontando un piccolo problema come se fosse eseguito due o tre volte nell'ultima riga. come farlo eseguire una sola volta quando raggiunge l'ultima riga di tableview?
R. Mohan

Grazie mille!! Ho inserito questo codice in scrollViewWillBeginDecelerating e viene eseguito solo una volta quando si scorre fino all'ultima riga.
Manali

20

Nessuna delle risposte sopra mi ha aiutato, quindi ho pensato a questo:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)aScrollView
{   
    NSArray *visibleRows = [self.tableView visibleCells];
    UITableViewCell *lastVisibleCell = [visibleRows lastObject];
    NSIndexPath *path = [self.tableView indexPathForCell:lastVisibleCell];
    if(path.section == lastSection && path.row == lastRow)
    {
        // Do something here
    }
}

Puoi spiegare da dove proviene "lastSection"?
ainesophaur

questo è per la visualizzazione a scorrimento e non per la tabella
iosMentalist

19

Usa – tableView:willDisplayCell:forRowAtIndexPath:( UITableViewDelegatemetodo)

Confronta semplicemente il indexPathcon gli elementi nella tua matrice di dati (o qualsiasi origine dati che utilizzi per la visualizzazione tabella) per capire se l'ultimo elemento viene visualizzato.

Documenti: http://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/UITableViewDelegate/tableView:willDisplayCell:forRowAtIndexPath :


Quindi, se ricarichi i dati di tableView, rimarrai bloccato in un ciclo infinito.
Dielson Sales

14

UITableViewè una sottoclasse di UIScrollViewe è UITableViewDelegateconforme a UIScrollViewDelegate. Quindi il delegato che colleghi alla vista tabella riceverà eventi come scrollViewDidScroll:e puoi chiamare metodi come contentOffsetnella vista tabella per trovare la posizione di scorrimento.


Sì, è quello che sto cercando, implementando quel delegato e verificando se la posizione di scorrimento è UITableViewScrollPositionBottom, saprò quando la tabella scorre verso il basso, grazie mille
Son Nguyen

8
NSLog(@"%f / %f",tableView.contentOffset.y, tableView.contentSize.height - tableView.frame.size.height);

if (tableView.contentOffset.y == tableView.contentSize.height - tableView.frame.size.height)
        [self doSomething];

Bello e semplice


8

in Swiftsi può fare qualcosa di simile. La seguente condizione sarà vera ogni volta che raggiungi la fine della visualizzazione tabella

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

1
il problema è quando tableview ha solo 3 celle, ad esempio println("came to last row")questo codice viene eseguito!
Mc.Lover

@ Mc.Lover questo era il problema esatto per me per il quale l'utilizzo della CPU stava per raggiungere il 92%. La risposta di neoneye ha funzionato per me. Ho anche aggiunto una versione rapida, se necessario.
Anuran Barman

7

Basandosi sulla risposta di @Jay Mayu, che ho ritenuto fosse una delle soluzioni migliori:

Obiettivo-C

// UITableViewDelegate
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {

// Need to call the service & update the array
if(indexPath.row + 1 == self.sourceArray.count) {
    DLog(@"Displayed the last row!");
  }
}

Swift 2.x

// UITableViewDelegate
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
    if (indexPath.row + 1) == sourceArray.count {
        print("Displayed the last row!")
    }
}

5

Generalmente lo uso per caricare più dati, quando l'ultima cella inizia a essere visualizzata

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
   if (indexPath.row ==  myDataArray.count-1) {
        NSLog(@"load more");
    }
}

1
questo è sbagliato. Ogni volta che chiami reloadData verrà effettuata una richiesta. E a volte devi solo ricaricare il tavolo.
Patrick Bassut

volevi dire, se sei nell'ultima cella e poi hai ricaricato i dati allora succederà?
Shaik Riyaz

5

Prendendo risposte eccellenti da neoneye ma in modo rapido e rinominando le variabili ..

Fondamentalmente sappiamo di aver raggiunto la parte inferiore della visualizzazione tabella se yOffsetAtBottomè oltre l'altezza del contenuto della tabella.

func isTableViewScrolledToBottom() -> Bool {
    let tableHeight = tableView.bounds.size.height
    let contentHeight = tableView.contentSize.height
    let insetHeight = tableView.contentInset.bottom

    let yOffset = tableView.contentOffset.y
    let yOffsetAtBottom = yOffset + tableHeight - insetHeight

    return yOffsetAtBottom > contentHeight
}

5

Ecco il codice della versione 3.0 di swift.

   func scrollViewDidScroll(_ scrollView: UIScrollView) {

        let offset = scrollView.contentOffset
        let bounds = scrollView.bounds
        let size = scrollView.contentSize
        let inset = scrollView.contentInset
        let y: Float = Float(offset.y) + Float(bounds.size.height) + Float(inset.bottom)
        let height: Float = Float(size.height)
        let distance: Float = 10

        if y > height + distance {
            // load more data
        }
    }

3

La mia soluzione è aggiungere celle prima che tableview deceleri sull'offset stimato. È prevedibile in base alla velocità.

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)offset {

    NSLog(@"offset: %f", offset->y+scrollView.frame.size.height);
    NSLog(@"Scroll view content size: %f", scrollView.contentSize.height);

    if (offset->y+scrollView.frame.size.height > scrollView.contentSize.height - 300) {
        NSLog(@"Load new rows when reaches 300px before end of content");
        [[DataManager shared] fetchRecordsNextPage];
    }
}

3

Aggiornamento per Swift 3

La risposta di Neoneye ha funzionato meglio per me nell'Obiettivo C, questo è l'equivalente della risposta in Swift 3:

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    let offset: CGPoint = scrollView.contentOffset
    let bounds: CGRect = scrollView.bounds
    let size: CGSize = scrollView.contentSize
    let inset: UIEdgeInsets = scrollView.contentInset
    let y: CGFloat = offset.y + bounds.size.height - inset.bottom
    let h: CGFloat = size.height
//        print("offset: %f", offset.y)
//        print("content.height: %f", size.height)
//        print("bounds.height: %f", bounds.size.height)
//        print("inset.top: %f", inset.top)
//        print("inset.bottom: %f", inset.bottom)
//        print("position: %f of %f", y, h)

    let reloadDistance: CGFloat = 10

    if (y > h + reloadDistance) {
            print("load more rows")
    }
}

2

Voglio eseguire alcune azioni sul mio 1 Tableviewcell completo.

Quindi il codice è collegare il:

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    NSArray* cells = self.tableView.visibleCells;
    NSIndexPath *indexPath = nil ;

    for (int aIntCount = 0; aIntCount < [cells count]; aIntCount++)
    {

        UITableViewCell *cell = [cells objectAtIndex:aIntCount];
CGRect cellRect = [self.tableView convertRect:cell.frame toView:self.tableView.superview];

        if (CGRectContainsRect(self.tableView.frame, cellRect))
        {
            indexPath = [self.tableView indexPathForCell:cell];

    //  remain logic
        }
     }
}

Possa questo essere d'aiuto a qualcuno.


0

La risposta di @ neoneye ha funzionato per me. Ecco la versione Swift 4 di esso

    let offset = scrollView.contentOffset
    let bounds = scrollView.bounds
    let size = scrollView.contentSize
    let insets = scrollView.contentInset
    let y = offset.y + bounds.size.height - insets.bottom
    let h = size.height
    let reloadDistance = CGFloat(10)
    if y > h + reloadDistance {
        //load more rows
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.