uitableviewcontroller uitableviewcell example iphone uitableview

iphone - uitableviewcell - Cómo detectar el final de la carga de UITableView



uitableviewcell lifecycle (19)

¿Está buscando la cantidad total de elementos que se mostrarán en la tabla o el total de elementos actualmente visibles? De cualquier manera ... Creo que el método ''viewDidLoad'' se ejecuta después de que se invocan todos los métodos del origen de datos. Sin embargo, esto solo funcionará en la primera carga de los datos (si está utilizando una sola asignación ViewController).

Quiero cambiar el desplazamiento de la tabla cuando finaliza la carga y ese desplazamiento depende de la cantidad de celdas cargadas en la mesa.

¿Hay algún modo en el SDK para saber cuándo ha finalizado una carga de uitableview? No veo nada en el delegado ni en los protocolos de fuente de datos.

No puedo usar el recuento de las fuentes de datos porque la carga de las celdas visibles solamente.


Aquí hay otra opción que parece funcionar para mí. En el método de delegado viewForFooter compruebe si es la sección final y agregue su código allí. Este enfoque vino a la mente después de darse cuenta de que willDisplayCell no tendrá en cuenta los pies de página si los tiene.

- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section { // Perform some final layout updates if (section == ([tableView numberOfSections] - 1)) { [self tableViewWillFinishLoading:tableView]; } // Return nil, or whatever view you were going to return for the footer return nil; } - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section { // Return 0, or the height for your footer view return 0.0; } - (void)tableViewWillFinishLoading:(UITableView *)tableView { NSLog(@"finished loading"); }

Creo que este enfoque funciona mejor si está buscando encontrar la carga final para toda la UITableView , y no simplemente las celdas visibles. Dependiendo de sus necesidades, es posible que solo desee las celdas visibles, en cuyo caso la respuesta de folex es una buena ruta.


Así es como lo haces en Swift 3:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { if indexPath.row == 0 { // perform your logic here, for the first row in the table } // .... }


C objetivo

[self.tableView reloadData]; [self.tableView performBatchUpdates:^{} completion:^(BOOL finished) { /// table-view finished reload }];

Rápido

self.tableView?.reloadData() self.tableView?.performBatchUpdates({ () -> Void in }, completion: { (Bool finished) -> Void in /// table-view finished reload })



En Swift puedes hacer algo como esto. La siguiente condición será verdadera cada vez que llegue al final de la tabla.

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


En iOS7.0x la solución es un poco diferente. Esto es lo que se me ocurrió.

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { BOOL isFinishedLoadingTableView = [self isFinishedLoadingTableView:tableView indexPath:indexPath]; if (isFinishedLoadingTableView) { NSLog(@"end loading"); } } - (BOOL)isFinishedLoadingTableView:(UITableView *)tableView indexPath:(NSIndexPath *)indexPath { // The reason we cannot just look for the last row is because // in iOS7.0x the last row is updated before // looping through all the visible rows in ascending order // including the last row again. Strange but true. NSArray * visibleRows = [tableView indexPathsForVisibleRows]; // did verify sorted ascending via logging NSIndexPath *lastVisibleCellIndexPath = [visibleRows lastObject]; // For tableviews with multiple sections this will be more complicated. BOOL isPreviousCallForPreviousCell = self.previousDisplayedIndexPath.row + 1 == lastVisibleCellIndexPath.row; BOOL isLastCell = [indexPath isEqual:lastVisibleCellIndexPath]; BOOL isFinishedLoadingTableView = isLastCell && isPreviousCallForPreviousCell; self.previousDisplayedIndexPath = indexPath; return isFinishedLoadingTableView; }


Esto es lo que haría.

  1. En su clase base (puede ser rootVC BaseVc, etc.),

    A. Escribe un protocolo para enviar la devolución de llamada "DidFinishReloading".

    @protocol ReloadComplition <NSObject> @required - (void)didEndReloading:(UITableView *)tableView; @end

    B. Escribe un método genérico para volver a cargar la vista de tabla.

    -(void)reloadTableView:(UITableView *)tableView withOwner:(UIViewController *)aViewController;

  2. En la implementación del método de clase base, llame a reloadData seguido de delegateMethod con retraso.

    -(void)reloadTableView:(UITableView *)tableView withOwner:(UIViewController *)aViewController{ [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [tableView reloadData]; if(aViewController && [aViewController respondsToSelector:@selector(didEndReloading:)]){ [aViewController performSelector:@selector(didEndReloading:) withObject:tableView afterDelay:0]; } }]; }

  3. Confirme el protocolo de finalización de recarga en todos los controladores de vista donde necesita la devolución de llamada.

    -(void)didEndReloading:(UITableView *)tableView{ //do your stuff. }

Referencia: https://discussions.apple.com/thread/2598339?start=0&tstart=0


Estoy copiando el código de Andrew y expandiéndolo para dar cuenta del caso donde solo tienes 1 fila en la tabla. ¡Está funcionando hasta ahora para mí!

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { // detect when all visible cells have been loaded and displayed // NOTE: iOS7 workaround used - see: http://.com/questions/4163579/how-to-detect-the-end-of-loading-of-uitableview?lq=1 NSArray *visibleRows = [tableView indexPathsForVisibleRows]; NSIndexPath *lastVisibleCellIndexPath = [visibleRows lastObject]; BOOL isPreviousCallForPreviousCell = self.previousDisplayedIndexPath.row + 1 == lastVisibleCellIndexPath.row; BOOL isLastCell = [indexPath isEqual:lastVisibleCellIndexPath]; BOOL isFinishedLoadingTableView = isLastCell && ([tableView numberOfRowsInSection:0] == 1 || isPreviousCallForPreviousCell); self.previousDisplayedIndexPath = indexPath; if (isFinishedLoadingTableView) { [self hideSpinner]; } }

NOTA: Solo estoy usando 1 sección del código de Andrew, así que tenlo en cuenta.


La respuesta @folex es correcta.

Pero fallará si TableView tiene más de una sección a la vez.

-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { if([indexPath isEqual:((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject])]){ //end of loading } }


Para la versión de respuesta elegida en Swift 3:

var isLoadingTableView = true func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if tableData.count > 0 && isLoadingTableView { if let indexPathsForVisibleRows = tableView.indexPathsForVisibleRows, let lastIndexPath = indexPathsForVisibleRows.last, lastIndexPath.row == indexPath.row { isLoadingTableView = false //do something after table is done loading } } }

Necesitaba la variable isLoadingTableView porque quería asegurarme de que la tabla terminara de cargarse antes de hacer una selección de celda predeterminada. Si no incluye esto, cada vez que recorra la tabla, volverá a invocar su código.


Prueba esta magia:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // cancel the perform request if there is another section [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(tableViewDidLoadRows:) object:tableView]; // create a perform request to call the didLoadRows method on the next event loop. [self performSelector:@selector(tableViewDidLoadRows:) withObject:tableView afterDelay:0]; return self.objects.count; } // called after the rows in the last section is loaded -(void)tableViewDidLoadRows:(UITableView*)tableView{ // make the cell selected after all rows loaded if(self.selectedObject){ NSInteger index = [self.objects indexOfObject:self.selectedObject]; [tableView selectRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] animated:NO scrollPosition:UITableViewScrollPositionMiddle]; } }

El comportamiento de la carga de la tabla significa que no puede llamar a seleccionar la fila hasta que la mesa conozca el recuento de filas y yo quería que se seleccionara una fila de manera predeterminada. Tenía un delegado de vista de tabla que no era un controlador de vista, así que no podía simplemente poner la celda de la tabla seleccionada en la vista que aparecía o cargar los métodos de delegado, y ninguna de las otras respuestas era de mi agrado.



Si tiene varias secciones, a continuación le mostramos cómo obtener la última fila en la última sección (Swift 3):

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if let visibleRows = tableView.indexPathsForVisibleRows, let lastRow = visibleRows.last?.row, let lastSection = visibleRows.map({$0.section}).last { if indexPath.row == lastRow && indexPath.section == lastSection { // Finished loading visible rows } } }


Siempre uso esta solución muy simple:

-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { if([indexPath row] == lastRow){ //end of loading //for example [activityIndicator stopAnimating]; } }


Solución Swift 2:

// willDisplay function override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { let lastRowIndex = tableView.numberOfRowsInSection(0) if indexPath.row == lastRowIndex - 1 { fetchNewDataFromServer() } } // data fetcher function func fetchNewDataFromServer() { if(!loading && !allDataFetched) { // call beginUpdates before multiple rows insert operation tableView.beginUpdates() // for loop // insertRowsAtIndexPaths tableView.endUpdates() } }


así es como lo hago en Swift 3

let threshold: CGFloat = 76.0 // threshold from bottom of tableView internal func scrollViewDidScroll(_ scrollView: UIScrollView) { let contentOffset = scrollView.contentOffset.y let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height; if (!isLoadingMore) && (maximumOffset - contentOffset <= threshold) { self.loadVideosList() } }


Swift 3 y 4 versión:

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { if let lastVisibleIndexPath = tableView.indexPathsForVisibleRows?.last { if indexPath == lastVisibleIndexPath { // do here... } } }


Mejore la respuesta de @RichX: lastRow puede ser tanto [tableView numberOfRowsInSection: 0] - 1 o ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row . Entonces el código será:

-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { if([indexPath row] == ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row){ //end of loading //for example [activityIndicator stopAnimating]; } }

ACTUALIZACIÓN: Bueno, el comentario de @ htafoya es correcto. Si desea que este código detecte el final de la carga de todos los datos desde la fuente, no lo haría, pero esa no es la pregunta original. Este código es para detectar cuándo se muestran todas las celdas que están destinadas a ser visibles. willDisplayCell: usa aquí para una interfaz de usuario más suave (la celda individual generalmente se muestra rápidamente después de willDisplay: llamada). También podría intentarlo con tableView:didEndDisplayingCell: