ios - resizing - Tamaño dinámico de celdas de vista de tabla utilizando restricciones de diseño automático
uitableviewautomaticdimension (2)
Actualizar
He revisado la pregunta completamente después de mis últimos hallazgos.
Gol
Mi objetivo es implementar el siguiente efecto:
- Hay una vista de tabla simple
- El usuario selecciona una fila
- La fila seleccionada se expande, revelando otra etiqueta debajo de la original
Tenga en cuenta que soy consciente de que esto se puede lograr insertando / eliminando celdas debajo del seleccionado, ya tengo una implementación exitosa usando ese método .
Esta vez, quiero intentar lograr esto usando las restricciones de diseño automático.
Estado actual
Tengo un proyecto de muestra disponible para que cualquiera lo revise , y también se abrió un problema . Para resumir, esto es lo que he intentado hasta ahora:
Tengo los siguientes puntos de vista como actores aquí:
- La vista de contenido de la celda
- Una vista superior, que contiene la etiqueta principal ("vista principal")
- Una vista inferior, debajo de la vista principal, que contiene la etiqueta inicialmente oculta ("vista de detalle")
He configurado las restricciones de diseño automático dentro de mi celda de la siguiente manera (tenga en cuenta que esto es estrictamente pseudo-lenguaje):
- mainView.top = contentView.top
- mainView.leading = contentView.leading
- mainView.trailing = contentView.trailing
- mainView.bottom = detailView.top
- detailView.leading = contentView.leading
- detailView.trailing = contentView.trailing
- detailView.bottom = contentView.bottom
- detailView.height = 0
Tengo una subclase UITableViewCell
personalizada, con varias salidas, pero la más importante aquí es una salida para la restricción de altura mencionada anteriormente: la idea aquí es establecer su constant
en 0 por defecto, pero cuando se selecciona la celda, configúrelo en 44 , por lo que se vuelve visible:
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
detailViewHeightConstraint.constant = selected ? detailViewDefaultHeight : 0
UIView.animateWithDuration(0.3) {
self.layoutIfNeeded()
}
}
Tengo el siguiente resultado:
Entonces el efecto está funcionando, pero no exactamente como lo imaginé originalmente. En lugar de presionar la vista principal hacia arriba, quiero que la altura de la celda crezca cuando se muestre la vista de detalles, y que retroceda cuando esté oculta.
He examinado mi jerarquía de diseño durante el tiempo de ejecución:
- El estado inicial está bien. El alto de la vista de contenido es igual al alto de mi vista principal (en este caso, son 125 puntos).
- Cuando se selecciona la celda, la restricción de altura de la vista de detalles aumenta a 44 puntos y las dos vistas se apilan de forma vertical. Pero en lugar de ampliar la vista de contenido de la celda, en cambio, la vista principal se reduce.
Pregunta
Lo que necesito es lo siguiente: la altura de la vista de contenido de la celda de la vista de tabla debe ser igual a
- el alto de la vista principal, cuando la restricción de altura de la vista de detalles es 0 (actualmente esto funciona)
- altura de vista principal + altura de vista de detalle cuando la restricción de altura de la vista de detalles está configurada correctamente (esto no funciona).
¿Cómo tengo que establecer mis restricciones para lograr eso?
Después de una cantidad significativa de investigación, creo que encontré la solución con la ayuda de este excelente artículo.
Estos son los pasos necesarios para hacer que el tamaño de la celda:
Dentro de las Vistas Principal y Detallada, he establecido originalmente que las etiquetas estén centradas horizontal y verticalmente. Esto no es suficiente para las celdas de auto-dimensionamiento. Lo primero que necesitaba era configurar mi diseño utilizando restricciones de espaciado vertical en lugar de una simple alineación:
Además, debe establecer la resistencia de compresión vertical del contenedor principal a 1000.
La vista detallada es un poco más complicada: además de crear las restricciones verticales apropiadas, también debe jugar con sus prioridades para alcanzar el efecto deseado:
- La Altura del contenedor de detalles está limitada a 44 puntos, pero para que sea opcional, establezca su prioridad en 999 (según los documentos, todo lo que sea inferior a "Requerido") se considerará como tal).
- Dentro del contenedor de detalles, configure las restricciones de espacio vertical y déles una prioridad de 998.
La idea principal es la siguiente:
- Por defecto, la celda está contraída. Para lograr esto, debemos establecer programáticamente la constante de la restricción de altura del contenedor de detalles en 0. Como su prioridad es más alta que las restricciones verticales dentro de la vista de contenido de la celda, esta última se ignorará, por lo que el contenedor de detalles estará oculto.
- Cuando seleccionamos la celda, queremos que se expanda. Esto significa que las restricciones verticales deben tomar el control: establecemos la restricción de altura del Contenedor de detalles de prioridad en algo bajo (utilicé 250), por lo que se ignorará a favor de las restricciones dentro de la vista de contenido.
Tuve que modificar mi subclase UITableViewCell
para admitir estas operaciones:
// `showDetails` is exposed to control, whether the cell should be expanded
var showsDetails = false {
didSet {
detailViewHeightConstraint.priority = showsDetails ? lowLayoutPriority : highLayoutPriority
}
}
override func awakeFromNib() {
super.awakeFromNib()
detailViewHeightConstraint.constant = 0
}
Para activar el comportamiento, debemos anular tableView(_:didSelectRowAtIndexPath:)
:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
switch expandedIndexPath {
case .Some(_) where expandedIndexPath == indexPath:
expandedIndexPath = nil
case .Some(let expandedIndex) where expandedIndex != indexPath:
expandedIndexPath = nil
self.tableView(tableView, didSelectRowAtIndexPath: indexPath)
default:
expandedIndexPath = indexPath
}
}
Tenga en cuenta que he introducido expandedIndexPath
para realizar un seguimiento de nuestro índice actualmente expandido:
var expandedIndexPath: NSIndexPath? {
didSet {
switch expandedIndexPath {
case .Some(let index):
tableView.reloadRowsAtIndexPaths([index], withRowAnimation: UITableViewRowAnimation.Automatic)
case .None:
tableView.reloadRowsAtIndexPaths([oldValue!], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
}
Establecer la propiedad dará como resultado que la vista de tabla vuelva a cargar los índices apropiados, dándonos una oportunidad perfecta para decirle a la celda, si se expande:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ExpandableTableViewCell
cell.mainTitle = viewModel.mainTitleForRow(indexPath.row)
cell.detailTitle = viewModel.detailTitleForRow(indexPath.row)
switch expandedIndexPath {
case .Some(let expandedIndexPath) where expandedIndexPath == indexPath:
cell.showsDetails = true
default:
cell.showsDetails = false
}
return cell
}
El último paso es habilitar el auto-tamaño en viewDidLoad()
:
override func viewDidLoad() {
super.viewDidLoad()
tableView.contentInset.top = statusbarHeight
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 125
}
Aquí está el resultado:
Las células ahora se dimensionan correctamente. Puede notar que la animación todavía es un poco extraña, pero corregir eso no entra dentro del alcance de esta pregunta.
Conclusión: esto fue mucho más difícil de lo que debería ser. 😀 Realmente espero ver algunas mejoras en el futuro.
Esto está en obj-c, pero estoy seguro de que lo manejarás:
Agregue su viewDidLoad:
self.tableView.estimatedRowHeight = self.tableView.rowHeight; self.tableView.rowHeight = UITableViewAutomaticDimension;
Esto habilitará celdas de tamaño propio para su tableView, y debería funcionar en iOS8 +