Comment savoir quand UITableView a fait défiler vers le bas sur iPhone


100

Je voudrais savoir quand un a UITableViewfait défiler vers le bas afin de charger et d'afficher plus de contenu, quelque chose comme un délégué ou autre chose pour informer le contrôleur quand le tableau a défilé vers le bas.

Est-ce que quelqu'un le sait, aidez-moi s'il vous plaît, merci d'avance!

Réponses:


18

Le meilleur moyen est de tester un point en bas de l'écran et d'utiliser cet appel de méthode chaque fois que l'utilisateur fait défiler ( scrollViewDidScroll):

- (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point

Testez un point près du bas de l'écran, puis en utilisant l'indexPath, il retourne vérifier si cet indexPath est la dernière ligne, puis si c'est le cas, ajoutez des lignes.


Malheureusement, mon application mettra à jour les données en temps réel pour les lignes existantes de ce tableau, de sorte que cette façon peut obtenir plus de contenu lorsque le tableau ne défile pas du tout.
Son Nguyen

247

dans le délégué tableview, faites quelque chose comme ça

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");
    }
}

Rapide:

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")
    }
}

Bien! Utilisez simplement: y> = h + reload_distance, ce qui n'est vraiment un problème que lorsque reload_distance = 0 (par exemple, pour les rebonds NO).
Chris Prince

4
Ce code dans "scrollViewDidScroll" est exécuté tout le temps que l'utilisateur déplace son doigt de haut en bas sur l'écran. Il est préférable de le déplacer sous "scrollViewDidEndDecelerating". De cette façon, il sera exécuté une fois lorsque l'utilisateur arrête de bouger son doigt.
Misha

hmm .. ce code fonctionne toujours? ne pas comprendre UITableView pour moi. J'aimerais connaître le raisonnement derrière tout le code ci-dessus, alors peut-être que je peux réparer
FlowUI. SimpleUITesting.com

offset: 237.000000 content.height: 855.000000 bounds.haight: 667.000000 insert.top: 64.000000 insert.bottom: 49.000000 pos: 855.000000 of 855.000000 if is getting false
Abhishek Thapliyal

62

Les neoneyes modifiés répondent un peu.

Cette réponse cible ceux d'entre vous qui souhaitent que l'événement ne soit déclenché qu'une seule fois par relâchement du doigt .

Convient lors du chargement de plus de contenu à partir d'un fournisseur de contenu (service Web, données de base, etc.). Notez que cette approche ne respecte pas le temps de réponse de votre service 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");
    }
}

Cela ne marche pas. Chaque fois que l'utilisateur fait défiler vers le bas, le "charger plus de lignes" est appelé. Cela se produit même lorsque l'utilisateur a déjà fait défiler vers le bas et attend que l'API renvoie des données.
Dean

2
Dean, la question était de savoir quand la vue du tableau défilait vers le bas. Que vous récupériez ou non des données à partir d'une API n'est pas pertinent, n'est-ce pas?
Eyeball

Mais il n'y a pas de message indiquant load more .. afin que l'utilisateur puisse être conscient qu'il y a plus de résultats à afficher.
iPhone 7

Bonne réponse dans mon cas, cela fonctionne si: float reload_distance = 10; if (y> (h - reload_distance)) {NSLog (@ "charger plus de lignes"); } parce que y = 565 et h est aussi 565
Abhishek Thapliyal

1
J'ai essayé la réponse de Eyeball et de @neoneye. Croyez-moi ou non «scrollViewDidScroll» appelé plusieurs fois par rapport à «scrollViewDidEndDragging». Il était donc efficace d'utiliser 'scrollViewDidEndDragging' car j'ai un appel API dessus.
Vinod Supnekar

39

ajoutez cette méthode dans le 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
J'aime cette solution sauf un petit détail. if (distanceFromBottom <= height)
Mark McCorkle

Cela m'a aidé à résoudre mon problème, merci !! une barre d'onglets !! c'était une barre d'onglets stupide !!
Dmytro

ce travail est un charme, et il est simple aussi, mais je suis confronté à un petit problème comme s'il s'exécute deux ou trois fois à la dernière ligne. comment le faire exécuter une seule fois lorsqu'il atteint la dernière ligne de tableview?
R. Mohan

Merci beaucoup!! J'ai mis ce code dans scrollViewWillBeginDecelerating et il ne s'exécute qu'une seule fois lorsqu'il est défilé jusqu'à la dernière ligne.
Manali

20

Aucune des réponses ci-dessus ne m'a aidé, alors j'ai trouvé ceci:

- (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
    }
}

Pouvez-vous préciser d'où vient "lastSection"?
ainesophaur

ceci est pour la vue de défilement et non la table
iosMentalist

19

Utiliser – tableView:willDisplayCell:forRowAtIndexPath:( UITableViewDelegateméthode)

Comparez simplement le indexPathavec les éléments de votre tableau de données (ou quelle que soit la source de données que vous utilisez pour votre vue de tableau) pour déterminer si le dernier élément est affiché.

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


Ensuite, si vous rechargez les données de tableView, vous serez coincé dans une boucle infinie.
Dielson Sales

14

UITableViewest une sous-classe de UIScrollViewet se UITableViewDelegateconforme à UIScrollViewDelegate. Ainsi, le délégué que vous attachez à la vue de table obtiendra des événements tels que scrollViewDidScroll:, et vous pouvez appeler des méthodes telles que contentOffsetsur la vue de table pour trouver la position de défilement.


Oui, c'est ce que je recherche, en implémentant ce délégué et en vérifiant si la position de défilement est UITableViewScrollPositionBottom, je saurai quand le tableau défile vers le bas, merci beaucoup
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];

Sympa et simple


8

en Swiftvous pouvez faire quelque chose comme ça. La condition suivante sera vraie chaque fois que vous atteindrez la fin de la tableview

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

1
le problème est lorsque tableview n'a que 3 cellules, par exemple, println("came to last row")ce code s'exécute!
Mc.Lover

@ Mc.Lover c'était exactement le problème pour moi pour lequel l'utilisation du processeur allait à 92%. La réponse de neoneye a fonctionné pour moi. J'ai également ajouté une version rapide pour cela si vous en avez besoin.
Anuran Barman

7

S'appuyant sur la réponse de @Jay Mayu, qui, selon moi, était l'une des meilleures solutions:

Objectif 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

J'utilise généralement cela pour charger plus de données, lorsque la dernière cellule commence à s'afficher

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

1
c'est faux. Chaque fois que vous appelez reloadData, une demande sera effectuée. Et parfois, il vous suffit de recharger la table.
Patrick Bassut

vous vouliez dire, si vous êtes dans la dernière cellule et que vous avez ensuite rechargé les données, cela se produira?
Shaik Riyaz

5

Prendre neoneye d' excellentes réponses mais rapide, et renommer des variables ..

Fondamentalement, nous savons que nous avons atteint le bas de la vue de tableau si le yOffsetAtBottomest au-delà de la hauteur du contenu de la table.

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

Voici le code de la version Swift 3.0.

   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

Ma solution consiste à ajouter des cellules avant que la vue du tableau ne ralentisse sur le décalage estimé. C'est prévisible par vitesse.

- (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

Mise à jour pour Swift 3

La réponse de Neoneye a fonctionné le mieux pour moi en Objectif C, c'est l'équivalent de la réponse dans 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

Je veux effectuer une action sur mon 1 Tableviewcell complète.

Donc, le code est lié au:

-(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
        }
     }
}

Puisse cela aider quelqu'un.


0

La réponse de @ neoneye a fonctionné pour moi. En voici la version Swift 4

    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
    }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.