UITableView desplazamiento infinito

¿Cómo hago un desplazamiento infinito en una UITableView ? Sé cómo hacerlo usando UIScrollView , en el que Apple ha demostrado en uno de los videos de la WWDC. Intenté hacer lo siguiente en tableView:cellForRowAtIndexPath: :

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

pero esto falla. ¿Alguna otra idea?

Si necesita saber cuándo toca la parte inferior de UITableView, conviértase en delegado (porque es una subclase de UIScrollView) y use el método -scrollViewDidScroll: delegate para comparar la altura del contenido de la tabla y su posición de desplazamiento real.

EDITAR (algo como esto):

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

Puede soportar scroll infinito con pull para actualizar en la parte superior y / o desplazarse continuamente en la parte inferior con una rueda giratoria usando:

https://github.com/samvermette/SVPullToRefresh

SVPullToRefresh maneja la lógica cuando UITableView llega al final. Se muestra una rueda giratoria automáticamente y se dispara un bloque de callback. Agregas tu lógica de negocios al bloque de callback.

Aquí hay un ejemplo:

 #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 }]; 

Este proyecto se puede agregar a su proyecto usando CocoaPods o directamente comstackdo en su proyecto.

Aquí hay una demostración muy rápida y completa de un UITableView de desplazamiento infinito que armé …

 @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’ es lo mismo que ‘UIScrollView’ en el método ‘scrollViewDidScroll’.

Entonces, es fácil emular el desplazamiento infinito.

  1. doblar la matriz para que la cabeza y la cola se unan para emular la mesa circular

  2. use mi siguiente código para hacer que el usuario cambie entre la primera parte de la tabla duplicada y la segunda parte de la tabla duplicada cuando tienden a llegar al inicio o al final de la tabla.

:

 /* 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))); } } 

PD: he usado este código en una de mis aplicaciones llamada NT Time Table (Lite). Si desea la vista previa, puede ver la aplicación: https://itunes.apple.com/au/app/nt-time-table-lite/id528213278?mt=8

Si su tabla a veces puede ser demasiado corta, al comienzo del método anterior puede agregar una lógica if para salir del método cuando el conteo de datos es, por ejemplo, menor que 9.

Para mí funcionó mejor scrollViewDidEndDragging: que scrollViewDidScroll:.

El segundo enfoque le enviará cada posición durante el desplazamiento y, si está buscando recursos remotos, llegará a su punto final varias veces, lo que no es bueno.

Ejemplo completo basado en la solución @codafi con comentarios de @danielgomezrico sobre cómo calcular el contenidoAltura :

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

Generalmente scrollViewDidEndDecelerating y dentro de él pongo mi código para solicitar más datos.
Ejemplo:

 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView{ float endScrolling = scrollView.contentOffset.y + scrollView.frame.size.height; if (endScrolling >= scrollView.contentSize.height){ //put here your code } } 

Recientemente cargué en GitHub una subclase de UITableView, que implementa el desplazamiento infinito.
Puedes descargarlo aquí:
https://github.com/alchimya/iOS-LazyTableView

en lugar de anular, podemos hacer esto de manera óptima en layoutSubviews. Así es como lo implementé. Puede obtener más información sobre la implementación aquí

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

Uno de los simples y que me ofreció todo lo que necesito es esta clase:

https://github.com/jakemarsh/JMStatefulTableViewController

Solo necesita subclasificar JMStatefulTableViewController y tiene 3 métodos que debe sobrescribir:

  • uno que se llama en init, para obtener los datos iniciales
    • statefulTableViewControllerWillBeginInitialLoading
  • uno cuando el usuario tire para actualizar
    • statefulTableViewControllerWillBeginLoadingFromPullToRefresh
  • uno cuando se llama para el desplazamiento infinito (página siguiente)
    • statefulTableViewControllerWillBeginLoadingNextPage

Esto también se puede usar desde Cocoapods .