ios uitableview swift ios8

ios - Desplazamiento desigual después de actualizar UITableViewCell en su lugar con UITableViewAutomaticDimension



swift ios8 (6)

Aquí está la mejor solución que encontré para resolver este tipo de problema (problema de desplazamiento + reloadRows + iOS 8 UITableViewAutomaticDimension);

Consiste en mantener todas las alturas en un diccionario y actualizarlas (en el diccionario) ya que tableView mostrará la celda.

Luego devolverá la altura guardada en - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath Método - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath .

Deberías implementar algo como esto:

C objetivo

- (void)viewDidLoad { [super viewDidLoad]; self.heightAtIndexPath = [NSMutableDictionary new]; self.tableView.rowHeight = UITableViewAutomaticDimension; } - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { NSNumber *height = [self.heightAtIndexPath objectForKey:indexPath]; if(height) { return height.floatValue; } else { return UITableViewAutomaticDimension; } } - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { NSNumber *height = @(cell.frame.size.height); [self.heightAtIndexPath setObject:height forKey:indexPath]; }

Swift 3

@IBOutlet var tableView : UITableView? var heightAtIndexPath = NSMutableDictionary() override func viewDidLoad() { super.viewDidLoad() tableView?.rowHeight = UITableViewAutomaticDimension } func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { if let height = heightAtIndexPath.object(forKey: indexPath) as? NSNumber { return CGFloat(height.floatValue) } else { return UITableViewAutomaticDimension } } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { let height = NSNumber(value: Float(cell.frame.size.height)) heightAtIndexPath.setObject(height, forKey: indexPath as NSCopying) }

Estoy creando una aplicación que tiene una vista de feed para las publicaciones enviadas por los usuarios. Esta vista tiene un UITableView con una implementación personalizada de UITableViewCell . Dentro de esta celda, tengo otro UITableView para mostrar comentarios. La esencia es algo como esto:

Feed TableView PostCell Comments (TableView) CommentCell PostCell Comments (TableView) CommentCell CommentCell CommentCell CommentCell CommentCell

El feed inicial se descargará con 3 comentarios para obtener una vista previa, pero si hay más comentarios, o si el usuario agrega o elimina un comentario, quiero actualizar PostCell en su lugar dentro de la vista de la tabla de feed agregando o eliminando CommentCells a los comentarios mesa dentro del PostCell . Actualmente estoy usando el siguiente ayudante para lograr eso:

// (PostCell.swift) Handle showing/hiding comments func animateAddOrDeleteComments(startRow: Int, endRow: Int, operation: CellOperation) { let table = self.superview?.superview as UITableView // "table" is outer feed table // self is the PostCell that is updating it''s comments // self.comments is UITableView for displaying comments inside of the PostCell table.beginUpdates() self.comments.beginUpdates() // This function handles inserting/removing/reloading a range of comments // so we build out an array of index paths for each row that needs updating var indexPaths = [NSIndexPath]() for var index = startRow; index <= endRow; index++ { indexPaths.append(NSIndexPath(forRow: index, inSection: 0)) } switch operation { case .INSERT: self.comments.insertRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.None) case .DELETE: self.comments.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.None) case .RELOAD: self.comments.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: UITableViewRowAnimation.None) } self.comments.endUpdates() table.endUpdates() // trigger a call to updateConstraints so that we can update the height constraint // of the comments table to fit all of the comments self.setNeedsUpdateConstraints() } override func updateConstraints() { super.updateConstraints() self.commentsHeight.constant = self.comments.sizeThatFits(UILayoutFittingCompressedSize).height }

Esto logra la actualización muy bien. La publicación se actualiza en su lugar con comentarios agregados o eliminados dentro de PostCell como se esperaba. Estoy usando PostCells tamaño PostCells en la tabla de alimentación. La tabla de comentarios de PostCell expande para mostrar todos los comentarios, pero la animación es un poco irregular y la tabla se desplaza hacia arriba y hacia abajo una docena de píxeles más o menos mientras se realiza la animación de actualización de celda.

El salto durante el cambio de tamaño es un poco molesto, pero mi problema principal viene después. Ahora, si me desplazo hacia abajo en el feed, el desplazamiento es suave como antes, pero si me desplazo hacia arriba por encima de la celda que acabo de cambiar de tamaño después de agregar comentarios, el feed saltará hacia atrás varias veces antes de llegar a la parte superior del feed. Configuré las celdas de tamaño automático de iOS8 para el Feed de esta manera:

// (FeedController.swift) // tableView is the feed table containing PostCells self.tableView.rowHeight = UITableViewAutomaticDimension self.tableView.estimatedRowHeight = 560

Si elimino el estimatedRowHeight , la tabla se desplaza hacia la parte superior cada vez que cambia la altura de una celda. Me siento bastante atrapado en esto ahora y, como nuevo desarrollador de iOS, podría usar cualquier consejo que pueda tener.


Estaba enfrentando el mismo problema también. Encontré una solución, pero no soluciona por completo el tirón. Pero parece ser mucho mejor en comparación con el desplazamiento entrecortado anterior.

En su método delegado UITableView :cellForRowAtIndexPath: intente usar los dos métodos siguientes para actualizar las restricciones antes de devolver la celda. (Lenguaje rápido)

cell.setNeedsUpdateConstraints() cell.updateConstraintsIfNeeded()

EDITAR: También puede que tenga que jugar con el valor tableView.estimatedRowHeight para obtener un desplazamiento más suave.


La solución @dosdos funciona bien

pero hay algo que deberías agregar

siguiendo la respuesta de @dosdos

Swift 3/4

@IBOutlet var tableView : UITableView! var heightAtIndexPath = NSMutableDictionary() override func viewDidLoad() { super.viewDidLoad() tableView?.rowHeight = UITableViewAutomaticDimension } func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { if let height = heightAtIndexPath.object(forKey: indexPath) as? NSNumber { return CGFloat(height.floatValue) } else { return UITableViewAutomaticDimension } } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { let height = NSNumber(value: Float(cell.frame.size.height)) heightAtIndexPath.setObject(height, forKey: indexPath as NSCopying) }

luego use estas líneas cuando quiera, para mí las uso dentro de textDidChange

  1. primera recarga Tableview
  2. restricción de actualización
  3. finalmente pasar a la parte superior de Tableview

    tableView.reloadData() self.tableView.layoutIfNeeded() self.tableView.setContentOffset(CGPoint.zero, animated: true)


Siguiendo la respuesta de @dosdos .

También me pareció interesante implementar: tableView(tableView: didEndDisplayingCell: forRowAtIndexPath:

Especialmente para mi código, donde la celda está cambiando las restricciones dinámicamente mientras la celda ya se muestra en la pantalla. Actualizar el diccionario de esta manera ayuda la segunda vez que se muestra la celda.

var heightAtIndexPath = [NSIndexPath : NSNumber]() .... tableView.rowHeight = UITableViewAutomaticDimension tableView.estimatedRowHeight = UITableViewAutomaticDimension .... extension TableViewViewController: UITableViewDelegate { //MARK: - UITableViewDelegate func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { let height = heightAtIndexPath[indexPath] if let height = height { return CGFloat(height) } else { return UITableViewAutomaticDimension } } func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { let height: NSNumber = CGRectGetHeight(cell.frame) heightAtIndexPath[indexPath] = height } func tableView(tableView: UITableView, didEndDisplayingCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { let height: NSNumber = CGRectGetHeight(cell.frame) heightAtIndexPath[indexPath] = height } }


Tuvimos el mismo problema. Proviene de una mala estimación de la altura de la celda que hace que el SDK fuerce una mala altura, lo que provocará el salto de las células al desplazarse hacia arriba. Dependiendo de cómo construyó su celda, la mejor manera de solucionar esto es implementar el método UITableViewDelegate - (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath

Mientras su estimación sea bastante cercana al valor real de la altura de la celda, esto casi cancelará los saltos y las sacudidas. Así es como lo implementamos, obtendrá la lógica:

- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath { // This method will get your cell identifier based on your data NSString *cellType = [self reuseIdentifierForIndexPath:indexPath]; if ([cellType isEqualToString:kFirstCellIdentifier]) return kFirstCellHeight; else if ([cellType isEqualToString:kSecondCellIdentifier]) return kSecondCellHeight; else if ([cellType isEqualToString:kThirdCellIdentifier]) return kThirdCellHeight; else { return UITableViewAutomaticDimension; } }

Soporte Swift 2 agregado

func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { // This method will get your cell identifier based on your data let cellType = reuseIdentifierForIndexPath(indexPath) if cellType == kFirstCellIdentifier return kFirstCellHeight else if cellType == kSecondCellIdentifier return kSecondCellHeight else if cellType == kThirdCellIdentifier return kThirdCellHeight else return UITableViewAutomaticDimension }


la respuesta de dosdos funcionó para mí en Swift 2

Declara el ivar

var heightAtIndexPath = NSMutableDictionary()

en func viewDidLoad ()

func viewDidLoad() { .... your code self.tableView.rowHeight = UITableViewAutomaticDimension }

Luego agregue los siguientes 2 métodos:

override func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat { let height = self.heightAtIndexPath.objectForKey(indexPath) if ((height) != nil) { return CGFloat(height!.floatValue) } else { return UITableViewAutomaticDimension } } override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { let height = cell.frame.size.height self.heightAtIndexPath.setObject(height, forKey: indexPath) }

SWIFT 3:

var heightAtIndexPath = [IndexPath: CGFloat]() func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { return self.heightAtIndexPath[indexPath] ?? UITableViewAutomaticDimension } func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { self.heightAtIndexPath[indexPath] = cell.frame.size.height }