Estoy creando una aplicación que tiene una vista de noticias en tiempo real para publicaciones enviadas por usuarios. Esta vista tiene una implementación UITableView
personalizada UITableViewCell
. Dentro de esta celda, tengo otra UITableView
para mostrar comentarios. La esencia es algo como esto:
Feed TableView
PostCell
Comments (TableView)
CommentCell
PostCell
Comments (TableView)
CommentCell
CommentCell
CommentCell
CommentCell
CommentCell
La fuente 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 el contenido PostCell
dentro de la vista de la tabla de fuentes agregando o eliminando CommentCells
la tabla de comentarios en el interior. 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 del PostCell
como se esperaba. Estoy usando el tamaño automático PostCells
en la mesa de alimentación. La tabla de comentarios se PostCell
expande para mostrar todos los comentarios, pero la animación es un poco desigual y la tabla se desplaza hacia arriba y hacia abajo una docena de píxeles aproximadamente mientras se lleva a cabo la animación de actualización de la 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 la fuente, 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, la fuente saltará hacia atrás unas cuantas veces antes de llegar a la parte superior de la fuente. Configuré iOS8
celdas de tamaño automático 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 simplemente 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.
fuente
Tuvimos el mismo problema. Proviene de una mala estimación de la altura de la celda que hace que el SDK fuerce una altura incorrecta, lo que provocará el salto de las celdas al retroceder. Dependiendo de cómo construyó su celda, la mejor manera de solucionar esto es implementar el
UITableViewDelegate
método- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
Siempre que su estimación esté bastante cerca del 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; } }
Se agregó soporte para Swift 2
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 }
fuente
heightForRowAtIndexPath
, pero todavía tenía unUITableView
desplazamiento entrecortado . Seguir la respuesta de @GabrielCartier y agregar una lógica más específica según el tipo de celda realmente ayudó y resolvió el problema, ¡gracias!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 }
fuente
La solución @dosdos está funcionando bien
pero hay algo que deberías agregar
siguiendo la respuesta de @dosdos
Rápido 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 usa estas líneas cuando quieras, para mí las uso dentro de textDidChange
finalmente mover a la vista de tabla superior
tableView.reloadData() self.tableView.layoutIfNeeded() self.tableView.setContentOffset(CGPoint.zero, animated: true)
fuente
Yo también estaba enfrentando el mismo problema. Encontré una solución, pero no soluciona completamente el tirón. Pero parece ser mucho mejor en comparación con el desplazamiento entrecortado anterior.
En su
UITableView
método de delegado:cellForRowAtIndexPath:
, intente utilizar los dos métodos siguientes para actualizar las restricciones antes de devolver la celda. (Lenguaje rápido)EDITAR: Es posible que también tenga que jugar con el
tableView.estimatedRowHeight
valor para obtener un desplazamiento más suave.fuente
Siguiendo la respuesta de @dosdos .
También me pareció interesante implementar:
tableView(tableView: didEndDisplayingCell: forRowAtIndexPath:
Especialmente para mi código, donde la celda cambia 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 } }
fuente