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?
Si vous devez savoir quand vous atteignez le bas de UITableView, devenez son 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 réelle de défilement.
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];
}
}
Vous pouvez prendre en charge le défilement infini avec pull pour rafraîchir en haut et/ou faire défiler en continu en bas avec une molette en utilisant:
https://github.com/samvermette/SVPullToRefresh
SVPullToRefresh
gère la logique lorsque UITableView
atteint le bas. Un spinner s'affiche 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 ou directement compilé dans votre projet.
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
'UITableView' est identique à 'UIScrollView' dans la méthode 'scrollViewDidScroll'.
Ainsi, son défilement infini est facile à émuler.
doubler le tableau de sorte que la tête et la queue sont réunies pour émuler la table circulaire
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 consulter 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.
Pour moi, cela fonctionnait mieux scrollViewDidEndDragging: que scrollViewDidScroll:.
La deuxième approche vous enverra chaque position pendant le défilement et, si vous récupérez des ressources distantes, vous toucherez votre point de terminaison plusieurs fois, ce qui n'est pas bon.
Exemple complet basé sur la solution @ codafi avec les commentaires de @ danielgomezrico sur la façon dont pour 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];
}
}
En général, 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
plutôt que de remplacer, nous pouvons le faire de manière optimale dans layoutSubviews. Voici comment je l'ai implémenté. 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]];
}
}
}
}
}
L'un des plus simples et qui m'a offert tout ce dont j'ai besoin est cette classe:
https://github.com/jakemarsh/JMStatefulTableViewController
Il vous suffit de sous-classer JMStatefulTableViewController et il a 3 méthodes que vous devez écraser:
Cela peut également être utilisé à partir de Cocoapods .
scrollviewDidScroll appellera lorsque vous vous déplacerez dans les lignes dans tableview
func scrollViewDidScroll(_ scrollView: UIScrollView) {
//check for the visible rows
let indexpath = self.tableView.indexPathsForVisibleRows?.last
//check if the visible row last is equal to the total number of counts
if(indexpath?.last == self.listCount){
//code for adding data to the tableview and reload the table view.
}
}
regardez dans le lien pour plus de détails sur indexPathForVisibleRows https://developer.Apple.com/documentation/uikit/uitableview/1614885-indexpathsforvisiblerows