Altura de celda automática de iOS 8: no se puede desplazar a la última fila

Estoy usando iOS 8 nuevas células de auto-tamaño. Visualmente funciona bien: cada celda tiene su tamaño correcto. Sin embargo, si trato de desplazarme a la última fila , la vista de tabla no parece conocer su tamaño correcto. ¿Es esto un error o hay una solución para eso?

A continuación, se explica cómo recrear el problema:

Usando este proyecto – TableViewCellWithAutoLayoutiOS8 (referenciado a partir de esta respuesta SO ), obtuve las celdas de cambio de tamaño automático como se esperaba.

Sin embargo, si estoy llamando a la función scrollToRowAtIndexPath , así:

tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: model.dataArray.count - 1, inSection: 0), atScrollPosition: .Bottom, animated: true) 

No llegué a la última fila , solo me lleva a la mitad de camino.

Incluso tratando de usar una función de nivel inferior como esta:

 tableView.setContentOffset(CGPointMake(0, tableView.contentSize.height - tableView.frame.size.height), animated: true) 

El resultado no es el esperado, no llegará hasta el final. Si hago clic varias veces o espero unos momentos, eventualmente llegará al lugar correcto. Parece que tableView.contentSize.height no está configurado correctamente, por lo que el iOS “no sabe” dónde está esa última celda.

Agradecería cualquier ayuda.

Gracias

Actualización: 24 de junio de 2015

Apple ha solucionado la mayoría de estos errores a partir del iOS 9.0 SDK. Todos los problemas se corrigieron a partir de iOS 9 beta 2, incluido el desplazamiento a la parte superior e inferior de la vista de tabla sin animación, y la invocación de reloadData mientras se desplazaba en el medio de la vista de tabla.

Aquí están los problemas restantes que aún no se han solucionado:

  1. Cuando se usa una altura de fila estimada grande, desplazarse a la última fila con animación hace que las celdas de la vista de tabla desaparezcan.
  2. Al usar una altura de fila estimada pequeña, desplazarse a la última fila con animación hace que la vista de tabla termine de desplazarse demasiado pronto, dejando algunas celdas debajo del área visible (y la última fila aún fuera de la pantalla).

Se ha presentado un nuevo informe de errores (rdar: // 21539211) para estos problemas relacionados con el desplazamiento con animación.

Respuesta original

Este es un error de Apple con la estimación de altura de fila de la vista de tabla, y ha existido desde que esta funcionalidad se introdujo por primera vez en iOS 7. He trabajado directamente con los ingenieros de Apple UIKit y los desarrolladores evangelistas en este tema. Han reconocido que es un error, pero no tiene ninguna solución confiable (salvo la deshabilitación de la estimación de la altura de fila), y no parece especialmente interesado en solucionarlo.

Tenga en cuenta que el error se manifiesta de otras maneras, como la desaparición de las celdas de la vista de tabla cuando se llama a reloadData mientras se desplaza parcial o totalmente hacia abajo (por ejemplo, contentOffset.y es significativamente mayor que 0).

Claramente, con las celdas de auto-dimensionamiento iOS 8, la estimación de la altura de fila es críticamente importante, por lo que Apple realmente necesita abordar esto lo antes posible.

Archivé este problema el 21 de octubre de 2013 como Radar # 15283329. Haga un archivo de informes de errores duplicados para que Apple priorice una solución.

Puede adjuntar este proyecto de muestra simple para demostrar el problema. Se basa directamente en el código de muestra propio de Apple.

Este ha sido un error muy molesto, pero creo que encontré una solución permanente, aunque no puedo explicar por qué.

Llame a la función después de un pequeño retraso (inadvertido):

 let delay = 0.1 * Double(NSEC_PER_SEC) let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay)) dispatch_after(time, dispatch_get_main_queue(), { tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: model.dataArray.count - 1, inSection: 0), atScrollPosition: .Bottom, animated: true) }) 

Dime si esto también funciona para ti.

Definitivamente es un error de Apple. También tengo este problema. Resolví este problema llamando al método “scrollToRowAtIndexPath” dos veces el código de ejemplo es:

  if array.count > 0 { let indexPath: NSIndexPath = NSIndexPath(forRow: array.count - 1, inSection: 0) self.tblView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true) let delay = 0.1 * Double(NSEC_PER_SEC) let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay)) dispatch_after(time, dispatch_get_main_queue(), { self.tblView.scrollToRowAtIndexPath(indexPath, atScrollPosition: .Bottom, animated: true) }) } 

Encontré una solución temporal que podría ser útil hasta que Apple decida arreglar los muchos errores que nos han estado afectando.

 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *text = [self findTextForIndexPath:indexPath]; UIFont *font = [UIFont fontWithName:@"HelveticaNeue" size:13]; CGRect estimatedHeight = [text boundingRectWithSize:CGSizeMake(215, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: font} context:nil]; return TOP_PADDING + CGRectGetHeight(estimatedHeight) + BOTTOM_PADDING; } 

Esto no es perfecto, pero hizo el trabajo por mí. Ahora puedo llamar:

 - (void)scrollToLastestSeenMessageAnimated:(BOOL)animated { NSInteger count = [self tableView:self.tableView numberOfRowsInSection:0]; if (count > 0) { NSInteger lastPos = MAX(0, count-1); [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForItem:lastPos inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:animated]; } } 

En viewDidLayoutSubviews , encuentra el lugar correcto en la parte inferior (o una posición estimada muy cercana).

Espero que eso ayude.

Para mi caso, encontré una solución temporal al no sugerir una altura de celda estimada para el progtwig. Hice esto comentando el siguiente método en mi código:

 - (CGFloat) tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath 

Sin embargo, tenga en cuenta que hacerlo puede afectar la experiencia del usuario cuando el usuario se desplaza, si sus celdas varían mucho en comparación entre sí. Para mi caso, no hay diferencia notable hasta el momento.

¡Espero eso ayude!

Mi solución fue usar el tamaño del guión gráfico como estimación.

Entonces en vez de esto:

 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { return UITableViewAutomaticDimension; 

}

Hice algo como esto:

 - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { MyMessageType messageType = [self messageTypeForRowAtIndexPath:indexPath]; switch (messageType) { case MyMessageTypeText: return 45; break; case MyMessageTypeMaybeWithSomeMediaOrSomethingBiggerThanJustText: return 96; break; default: break; } } 

Estoy escribiendo una vista de tabla de chat, por lo que es probable que muchas de mis celdas, específicamente ese tipo de texto, sea más grande que lo que está en IB, especialmente si el mensaje de chat es muy largo. Esto parece ser bastante bueno … bueno … estimar y desplazar hacia abajo se acerca bastante. Parece ser un poco peor a medida que el desplazamiento se hace más largo, pero eso es de esperar, supongo

Simplemente llame a tableview reloadData después de que viewDidAppear pueda resolver el problema

 -(void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; [self.tableView reloadData]; } 

Desde la ventana del guión gráfico, haga clic en un área en blanco para anular la selección de todas las vistas, luego haga clic en la vista que tiene la vista de tabla y luego haga clic en el icono Resolve Auto Layout Issue y seleccione Reset to Suggested Constraints

enter image description hereenter image description here

Aunque como respuesta de smileyborg es un error en iOS 8.x, debe ser reparado en todas las plataformas que soporte …

Para solucionarlo antes de iOS9, el código siguiente funciona sin ningún dispatch_async o dispatch_after. Probado en el simulador de iOS 8.4.

ACTUALIZACIÓN : Llamar (solo) layout IfNeeded no funciona cuando el controlador de vista se vuelve visible por UIPageViewController que se está desplazando. Así que use layoutSubviews (o tal vez setNeedsLayout + layoutIfNeeded) en su lugar.

 // For iOS 8 bug workaround. // See https://stackoverflow.com/a/33515872/1474113 - (void)scrollToBottomForPreiOS9 { CGFloat originalY, scrolledY; do { // Lay out visible cells immediately for current contentOffset. // NOTE: layoutIfNeeded does not work when hosting UIPageViewController is dragged. [self.tableView layoutSubviews]; originalY = self.tableView.contentOffset.y; [self scrollToBottom]; // Call -scrollToRowAtIndexPath as usual. scrolledY = self.tableView.contentOffset.y; } while (scrolledY > originalY); } 

Tuve el mismo problema al crear un chat tableView con diferente altura de celdas. Llamo al código a continuación en el método del ciclo de vida viewDidAppear ():

 // First figure out how many sections there are let lastSectionIndex = self.tableView.numberOfSections - 1 // Then grab the number of rows in the last section let lastRowIndex = self.tableView.numberOfRowsInSection(lastSectionIndex) - 1 // Now just construct the index path let pathToLastRow = NSIndexPath(forRow: lastRowIndex, inSection: lastSectionIndex) // Make the last row visible self.tableView.scrollToRowAtIndexPath(pathToLastRow, atScrollPosition: UITableViewScrollPosition.None, animated: true) 

Por favor, avíseme si eso funcionó para usted también.

Use este código simple para desplazarse hacia abajo

  var rows:NSInteger=self.tableName.numberOfRowsInSection(0) if(rows > 0) { let indexPath = NSIndexPath(forRow: rows-1, inSection: 0) tableName.scrollToRowAtIndexPath(indexPath , atScrollPosition: UITableViewScrollPosition.Bottom, animated: true) } }