UITableView défilement infini

Comment faire un défilement infini dans un UITableView? Je sais comment le faire en utilisant un UIScrollView, dans lequel apple a démontré dans l'une des vidéos de la WWDC. J'ai essayé de faire ce qui suit dans tableView:cellForRowAtIndexPath::

if (indexPath.row == [self.newsFeedData_ count] - 1)
{
    [self.newsFeedData_ addObjectsFromArray:self.newsFeedData_];
    [self.tableView reloadData];
}

Mais cela échoue. Une autre idée?

39
demandé sur gardenofwine 2012-05-02 00:47:55

8 réponses

Si vous avez besoin de savoir quand vous appuyez sur le bas de UITableView, devenez délégué (car il s'agit d'une sous-classe de UIScrollView), et utilisez la méthode-scrollViewDidScroll: delegate pour comparer la hauteur du contenu de la table et sa position de défilement réelle.

EDIT (quelque chose comme ça):

- (void)scrollViewDidScroll:(UIScrollView *)scrollView_ 
{   
    CGFloat actualPosition = scrollView_.contentOffset.y;
    CGFloat contentHeight = scrollView_.contentSize.height - (someArbitraryNumber);
    if (actualPosition >= contentHeight) {
        [self.newsFeedData_ addObjectsFromArray:self.newsFeedData_];
        [self.tableView reloadData];
     }
}
55
répondu CodaFi 2012-08-08 01:12:08

Vous pouvez prendre en charge le défilement infini avec pull pour rafraîchir en haut et / ou faire défiler continuellement en bas avec une roue de spinner en utilisant:

Https://github.com/samvermette/SVPullToRefresh

SVPullToRefresh gère la logique lorsque UITableView atteint le fond. Un spinner est affiché automatiquement et un bloc de rappel est déclenché. Vous ajoutez votre logique métier au bloc de rappel.

Voici un exemple:

#import "UIScrollView+SVInfiniteScrolling.h"

// ...

[tableView addInfiniteScrollingWithActionHandler:^{
    // append data to data source, insert new cells at the end of table view
    // call [tableView.infiniteScrollingView stopAnimating] when done
}];

Ce projet peut être ajouté à votre projet en utilisant CocoaPods {[7] } ou directement compilé dans votre projet.

18
répondu Richard H Fung 2013-03-09 01:02:27

Voici une démo très rapide et complète d'un UITableView à défilement infini que j'ai mis en place...

@interface InfiniteScrollViewController ()

@property (nonatomic) NSMutableArray *tableViewData;
@property (nonatomic) BOOL loadingMoreTableViewData;

@end

@implementation InfiniteScrollViewController

- (void)viewDidLoad {
    self.tableViewData = [[NSMutableArray alloc] init];
    [self addSomeMoreEntriesToTableView];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return self.tableViewData.count + 1;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
    }

    if (indexPath.row < self.tableViewData.count) {
        cell.textLabel.text = [self.tableViewData objectAtIndex:indexPath.row];
    } else {
        cell.textLabel.text = @"Loading more data...";

        // User has scrolled to the bottom of the list of available data so simulate loading some more if we aren't already
        if (!self.loadingMoreTableViewData) {
            self.loadingMoreTableViewData = YES;
            [self performSelector:@selector(addSomeMoreEntriesToTableView) withObject:nil afterDelay:5.0f];
        }
    }

    return cell;
}

- (void)addSomeMoreEntriesToTableView {
    int loopTill = self.tableViewData.count + 20;
    while (self.tableViewData.count < loopTill) {
        [self.tableViewData addObject:[NSString stringWithFormat:@"%i", self.tableViewData.count]];
    };
    self.loadingMoreTableViewData = NO;
    [self.tableView reloadData];
}

@end
17
répondu Oliver Pearmain 2013-02-22 16:09:46

'UITableView' est identique à 'UIScrollView' dans la méthode' scrollViewDidScroll'.

Donc, il est facile d'émuler le défilement infini.

  1. Doublez le tableau pour que head et tail soient réunis pour émuler la table circulaire

  2. Utilisez mon code suivant pour faire basculer l'utilisateur entre la 1ère partie de la table doublée et la 2ème partie de la table doublée quand ils ont tendance à atteindre le début ou la fin de la table.

:

/* To emulate infinite scrolling...

The table data was doubled to join the head and tail: (suppose table had 1,2,3,4)
1 2 3 4|1 2 3 4 (actual data doubled)
---------------
1 2 3 4 5 6 7 8 (visualising joined table in eight parts)

When the user scrolls backwards to 1/8th of the joined table, user is actually at the 1/4th of actual data, so we scroll instantly (we take user) to the 5/8th of the joined table where the cells are exactly the same.

Similarly, when user scrolls to 6/8th of the table, we will scroll back to 2/8th where the cells are same. (I'm using 6/8th when 7/8th sound more logical because 6/8th is good for small tables.)

In simple words, when user reaches 1/4th of the first half of table, we scroll to 1/4th of the second half, when he reaches 2/4th of the second half of table, we scroll to the 2/4 of first half. This is done simply by subtracting OR adding half the length of the new/joined table.
*/


-(void)scrollViewDidScroll:(UIScrollView *)scrollView_ 
{  

    CGFloat currentOffsetX = scrollView_.contentOffset.x;
    CGFloat currentOffSetY = scrollView_.contentOffset.y;
    CGFloat contentHeight = scrollView_.contentSize.height;

    if (currentOffSetY < (contentHeight / 8.0)) {
    scrollView_.contentOffset = CGPointMake(currentOffsetX,(currentOffSetY + (contentHeight/2)));
    }
   if (currentOffSetY > ((contentHeight * 6)/ 8.0)) {
       scrollView_.contentOffset = CGPointMake(currentOffsetX,(currentOffSetY - (contentHeight/2)));
    }

}

P.S.-j'ai utilisé ce code sur une de mes applications appelée NT Time Table (Lite). Si vous voulez l'aperçu, vous pouvez vérifier l'application: https://itunes.apple.com/au/app/nt-time-table-lite/id528213278?mt=8

Si votre table peut parfois être trop courte, au début de la méthode ci-dessus, vous pouvez ajouter une logique if pour quitter la méthode lorsque le nombre de données est par exemple inférieur à 9.

12
répondu Anup Kattel 2016-07-22 02:46:47

Pour moi a mieux fonctionné scrollViewDidEndDragging: que scrollViewDidScroll:.

La deuxième approche vous enverra chaque position pendant le défilement et la cause, si vous récupérez des ressources distantes, vous atteindrez votre point de terminaison plusieurs fois, ce qui n'est pas bon.

Exemple Complet basé sur @codafi solution avec les commentaires de @danielgomezrico sur la façon de calculer contentHeight:

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
                  willDecelerate:(BOOL)decelerate {
    CGFloat actualPosition = scrollView.contentOffset.y;
    CGFloat contentHeight = scrollView.contentSize.height - (self.tableView.frame.size.height);
    if (actualPosition >= contentHeight) {
        // fetch resources
        [self.tableView reloadData];
    }
}
4
répondu zevarito 2015-08-24 22:53:09

Généralement, je remplace scrollViewDidEndDecelerating et à l'intérieur je mets mon code pour demander plus de données.
Exemple:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{

    float endScrolling = scrollView.contentOffset.y + scrollView.frame.size.height;

    if (endScrolling >= scrollView.contentSize.height){
        //put here your code

    }
}

Récemment, j'ai téléchargé sur GitHub une sous-classe de UITableView, qui implémente le défilement infini.
Vous pouvez le télécharger ici:
https://github.com/alchimya/iOS-LazyTableView

3
répondu Domenico Vacchiano 2015-07-16 12:40:42

Plutôt que de surcharger, nous pouvons le faire de manière optimale dans layoutSubviews. Voici comment je me suis mis en œuvre. Vous pouvez en savoir plus sur l'implémentation ici

- (void)layoutSubviews{
[super layoutSubviews];

if(self.delegateForViews){

    CGPoint contentOffset = self.contentOffset;

    if([self.delegateForViews noOfViews]>numOfReusableViews){
        NSUInteger centerIndex=visibleViews.count/2;
        NSUInteger noOfViews=[self.delegateForViews noOfViews];
        UIView *centerView=[visibleViews objectAtIndex:centerIndex];

        CGPoint centerViewOrigin=centerView.frame.origin;
        CGSize centerViewSize=centerView.frame.size;
        CGFloat offsetDifference=contentOffset.x-centerViewOrigin.x;
        CGFloat offsetDifferenceAbs=fabs(contentOffset.x-centerViewOrigin.x);

        if(offsetDifferenceAbs>=centerViewSize.width){

            if(offsetDifference<0){
                currentPosition--;
            }else{
                currentPosition++;
            }

            self.contentOffset=centerViewOrigin;

            currentPosition=[self getPosition:currentPosition noOfViews:noOfViews];

            [self.delegateForViews clearView:centerView];
            [self.delegateForViews setupView:centerView forPosition:currentPosition];

            for (int i=centerIndex-1; i>=0; i--) {
                UIView* prevView=[visibleViews objectAtIndex:i];
                [self.delegateForViews clearView:prevView];
                [self.delegateForViews setupView:prevView forPosition:
                        [self getPosition:currentPosition-1 noOfViews:noOfViews]];
            }

            for (int i=centerIndex+1; i<visibleViews.count; i++) {
                UIView* nextView=[visibleViews objectAtIndex:i];
                [self.delegateForViews clearView:nextView];
                [self.delegateForViews setupView:nextView forPosition:
                        [self getPosition:currentPosition+1 noOfViews:noOfViews]];
            }

        }
    }

}

}
1
répondu Shardul 2012-11-19 16:56:44

L'un des plus simples et qui m'a offert Tout ce dont j'ai besoin est cette classe:

Https://github.com/jakemarsh/JMStatefulTableViewController

Vous avez juste besoin de sous-classe JMStatefulTableViewController et il a 3 méthodes que vous devez écraser:

  • celui qui est appelé init, pour obtenir les données initiales
    • statefulTableViewControllerWillBegininitialloading
  • Un lorsque l'utilisateur tire vers rafraîchir
    • statefulTableViewControllerWillBeginloadingfrompulltorefresh
  • Un quand est appelé pour le défilement infini (page suivante)
    • statefulTableViewControllerWillBeginloadingnextpage

Cela peut également être utilisé à partir de Cocoapods.

0
répondu Cornel Damian 2014-01-21 12:20:12