ios - uitableviewcell - uitableviewautomaticdimension
Múltiples UILabels dentro de un UITableViewCell de tamaño propio (5)
El problema aquí es con la propiedad
preferredMaxLayoutWidth
las etiquetas de varias líneas.
Esta es la propiedad que le dice a la etiqueta cuándo debe ajustarse la palabra.
Debe configurarse correctamente para que el tamaño de contenido
intrinsicContentSize
cada etiqueta tenga la altura correcta, que en última instancia es lo que utilizará el diseño automático para determinar la altura de la celda.
Xcode 6 Interface Builder introdujo una nueva opción para establecer esta propiedad en Automático . Desafortunadamente, hay algunos errores graves (a partir de Xcode 6.2 / iOS 8.2) en los que esto no se configura correctamente / automáticamente al cargar una celda desde una plumilla o Storyboard.
Para evitar este error, necesitamos tener el conjunto
preferredMaxLayoutWidth
MaxLayoutWidth para que sea exactamente igual al ancho final de la etiqueta una vez que se muestra en la vista de tabla.
Efectivamente, queremos hacer lo siguiente antes de devolver la celda de
tableView:cellForRowAtIndexPath:
::
cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)
cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)
cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)
La razón por la que solo agregar este código por sí solo no funciona es porque cuando estas 3 líneas de código se ejecutan en
tableView:cellForRowAtIndexPath:
estamos usando el ancho de cada etiqueta para establecer el ancho
preferredMaxLayoutWidth
tableView:cellForRowAtIndexPath:
; sin embargo, si verifica el ancho del etiquetas en este momento, el ancho de la etiqueta es totalmente diferente de lo que terminará siendo una vez que se muestre la celda y se hayan presentado sus subvistas.
¿Cómo hacemos que los anchos de las etiquetas sean precisos en este punto, para que reflejen su ancho final? Aquí está el código que hace que todo se una:
// Inside of tableView:cellForRowAtIndexPath:, after dequeueing the cell
cell.bounds = CGRect(x: 0, y: 0, width: CGRectGetWidth(tableView.bounds), height: 99999)
cell.contentView.bounds = cell.bounds
cell.layoutIfNeeded()
cell.nameLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.nameLabel.frame)
cell.idLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.idLabel.frame)
cell.actionsLabel.preferredMaxLayoutWidth = CGRectGetWidth(cell.actionsLabel.frame)
Bien, entonces, ¿qué estamos haciendo aquí? Bueno, notarás que hay 3 nuevas líneas de código agregadas. Primero, debemos establecer el ancho de la celda de esta vista de tabla para que coincida con el ancho real de la vista de tabla (esto supone que la vista de tabla ya se ha establecido y tiene su ancho final, que debería ser el caso). Efectivamente, solo estamos corrigiendo el ancho de la celda temprano, ya que la vista de tabla eventualmente lo hará.
También notará que estamos usando
99999
para la altura.
¿De qué trata eso?
Esa es una solución simple para el problema discutido en detalle
here
, donde si sus restricciones requieren más espacio vertical que la altura actual de contentView de la celda, obtendrá una excepción de restricción que en realidad no indica ningún problema real.
La altura de la celda o cualquiera de sus subvistas en realidad no importa en este momento, porque solo nos importa obtener los anchos finales para cada etiqueta.
A continuación, nos aseguramos de que el contentView de la celda tenga el mismo tamaño que acabamos de asignar a la celda en sí, al establecer los límites de contentView para que sean iguales a los límites de la celda. Esto es necesario porque todas las restricciones de diseño automático que ha creado son relativas a contentView, por lo que contentView debe tener el tamaño correcto para que se resuelvan correctamente. Solo establecer el tamaño de la celda manualmente no dimensiona automáticamente el contentView para que coincida.
Finalmente, forzamos un pase de diseño en la celda, lo que hará que el motor de diseño automático resuelva sus restricciones y actualice los marcos de todas las subvistas.
Dado que la celda y contentView ahora tienen los mismos anchos que tendrán en tiempo de ejecución en la vista de tabla, los anchos de las etiquetas también serán correctos, lo que significa que el conjunto de MaxXayoutWidth
preferredMaxLayoutWidth
para cada etiqueta será preciso y hará que la etiqueta se ajuste en el momento correcto , lo que, por supuesto, significa que las alturas de las etiquetas se establecerán correctamente cuando la celda se use en la vista de tabla.
Definitivamente, este es un error de Apple en UIKit que tenemos que solucionar por ahora (¡así que presente informes de errores de archivo con Apple para que prioricen una solución!).
Una nota final: esta solución tendrá problemas si el ancho de
contentView
la celda de la vista de tabla no extiende el ancho completo de la vista de tabla, por ejemplo, cuando hay un índice de sección a la derecha.
En este caso, deberá asegurarse de tener esto en cuenta manualmente al configurar el ancho de la celda; es posible que deba codificar estos valores, algo como:
let cellWidth = CGRectGetWidth(tableView.bounds) - kTableViewSectionIndexWidth
cell.bounds = CGRect(x: 0, y: 0, width: cellWidth, height: 99999)
En esta aplicación iOS 8 que estoy creando, tengo una vista de tabla y necesito que cambien su tamaño. Lo implementé usando Auto Layout y funciona. Casi. Así es como se ve ahora.
Hay 3 etiquetas dentro de una celda. Etiqueta principal que tiene el texto lorem ipsum. Subtítulo que tiene la cadena de números (Esas son dos etiquetas separadas. Puede ser confuso porque tienen el mismo color). Luego, la tercera etiqueta con el pequeño texto negro.
La primera etiqueta se redimensionó correctamente sin problemas y la segunda etiqueta se mueve hacia arriba y hacia abajo en consecuencia. Pero el problema es con la tercera etiqueta pequeña. Como puede ver, no cambia el tamaño para adaptarse a todo el texto.
Ahora está sucediendo algo extraño. Lo giro horizontalmente y aquí está.
Como hay espacio, la etiqueta muestra todo el texto que se supone que debe mostrar. Multa. Luego lo vuelvo al retrato.
Ahora la etiqueta pequeña se ha redimensionado para adaptarse a todo su texto, pero desborda los límites de las celdas. Intenté agrandar la celda pero no funcionó. Como se trata de celdas de tamaño propio, no creo que sea la forma correcta incluso.
Tampoco recibo ningún error o incluso advertencia sobre mis restricciones de diseño automático.
He establecido estas dos líneas de código en el método
viewDidLoad()
.
tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension
¿Alguien puede decirme qué podría estar haciendo mal aquí?
Dado que es difícil responder solo mirando imágenes y no tengo más código para publicar junto al fragmento anterior, cargué un proyecto ejecutable de Xcode que demuestra el problema here . (Hay 2 celdas personalizadas. Básicamente es la misma celda, solo la altura aumenta en la segunda).
He estado jugando con las restricciones de diseño automático, pero parece que no puedo hacer que esto funcione. Cualquier ayuda sería apreciada.
Gracias.
ACTUALIZAR:
Con la ayuda de este tutorial encontré algunos consejos útiles. Según él, cada subvista debe tener restricciones que fijen todos sus lados y debe haber restricciones que van de arriba a abajo, lo que ayuda al diseño automático para calcular la altura de la celda. En mi publicación original, tenía espacios verticales entre cada etiqueta, así que creo que esa es la razón por la que el diseño automático no pudo calcular la altura adecuada.
Entonces hice algunos cambios.
- Reduje el espacio vertical entre las etiquetas a 0 y establecí las restricciones de espacio vertical entre las etiquetas superiores y medias y las etiquetas medias e inferiores.
- Agregué restricciones principales, superiores y finales a la etiqueta superior.
- Al principio y al final de la etiqueta del medio.
- A la izquierda, al final, al final de la etiqueta inferior.
Ahora aquí hay otra parte extraña. Cuando lo ejecuté por primera vez, el problema del recorte de la etiqueta inferior sigue ahí.
Pero si giro el dispositivo a horizontal y lo vuelvo a colocar en vertical, todas las celdas se redimensionan correctamente para que se ajusten a ambas etiquetas.
Sin embargo, todavía no puedo entender por qué esto no sucede al principio. El proyecto Xcode actualizado está here .
Encontré el mismo problema que tú y encontré una solución simple para resolverlo.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// dequeue cell...
// do autolayout staffs...or if the autolayout rule has been set in xib, do nothing
[cell layoutIfNeeded];
return cell;
}
Y el auto-dimensionamiento funcionó bien. En mi código, coloqué dos etiquetas en vertical, ambas son de altura dinámica. La altura de la celda está configurada correctamente para contener las dos etiquetas.
Probé la solución fácil y elegante de "fogisland", y no funcionó. Afortunadamente descubrí que una línea adicional hace que funcione en todas las direcciones. Solo dígale al sistema que no solo sugiere un nuevo diseño (layoutIfNeeded), sino que lo solicita explícitamente (setNeedLayout)
cell.setNeedsLayout()
cell.layoutIfNeeded()
return cell
Suponiendo que no tiene ningún error con sus restricciones como lo han sugerido otros, este problema parece derivarse del uso de un UILabel que permite múltiples líneas junto con un UITableViewCellAccessory. Cuando iOS presenta la celda y determina la altura, no tiene en cuenta el cambio de desplazamiento en el ancho que se produce debido a este accesorio, y se obtiene el truncamiento donde no se esperaba.
Suponiendo que desea que UILabel extienda el ancho completo de la vista de contenido, escribí un método que soluciona esto para todos los tamaños de fuente
-(void)fixWidth:(UILabel *)label forCell:(UITableViewCell *)cell {
float offset = 0;
switch ([cell accessoryType]) {
case UITableViewCellAccessoryCheckmark:
offset = 39.0;
break;
case UITableViewCellAccessoryDetailButton:
offset = 47.0;
break;
case UITableViewCellAccessoryDetailDisclosureButton:
offset = 67.0;
break;
case UITableViewCellAccessoryDisclosureIndicator:
offset = 33.0;
break;
case UITableViewCellAccessoryNone:
offset = 0;
break;
}
[label setPreferredMaxLayoutWidth:CGRectGetWidth([[self tableView]frame]) - offset - 8];
}
Simplemente ponga esto en su cellForRowAtIndexPath
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
#Setup the cell
...
// Fix layout with accessory view
[self fixWidth:[cell label] forCell:cell];
return cell;
}
Haga esto para cualquier etiqueta que tenga múltiples líneas para ajustar el ancho correctamente y luego recalcular las alturas apropiadas. Esto también funciona con tamaños de fuente dinámicos.
Como había mencionado smileyborg, si no usaras todo el ancho de contentView, podrías hacer referencia a las restricciones y restarlas también del ancho.
Editar: anteriormente estaba ejecutando ''layoutIfNeeded'' en la celda, pero esto estaba creando problemas de rendimiento y no parecía ser necesario de todos modos. Eliminarlo no me ha causado ningún problema.
Tienes dos problemas aquí.
1 / En este momento, usando Visual Format Language, las restricciones verticales de su celda se pueden traducir de esta manera:
Cell: "V:|-(10)-[nameLabel]-(67)-|"
Luego, establece un segundo grupo de restricciones:
Cell: "V:|-(10)-[nameLabel]-(8)-[pnrLabel]-(2)-[actionsLabel]"
Esos dos grupos de restricciones no pueden mezclarse bien y revelarán su ambigüedad con su segundo problema.
2 / Por algunas razones,
actionsLabel
se limita a una línea cuando
actionsLabel
su aplicación.
Luego, cuando gira su dispositivo al modo horizontal,
actionsLabel
acepta que se muestre con dos líneas o más.
Luego, cuando gire su dispositivo nuevamente al modo vertical,
actionsLabel
sigue mostrando dos líneas o más.
Pero, debido a que
actionsLabel
no es realmente parte de las restricciones de altura de su celda, se superpone a los límites de su celda.
Si desea resolver todos esos problemas, le recomiendo primero que reconstruya su archivo xib desde cero.
Esto curará sus
actionsLabel
Comportamiento extraño de la
actionsLabel
(dos líneas o más solo cuando gira su dispositivo).
Luego, tendrá que definir sus restricciones de esta manera:
Cell: "V:|-(10)-[nameLabel(>=21)]-(8)-[pnrLabel(>=21)]-(2)-[actionsLabel(>=21)]-(10)-|"
Por supuesto, puede definir otras restricciones de altura mínima para sus etiquetas que
(>=21)
.
Del mismo modo, su margen inferior se puede establecer en otro valor que no sea
-(10)-
.
Apéndice
Para responder a su pregunta, creé un proyecto simple con el patrón de restricciones anterior en mi archivo .xib. La siguiente imagen puede ayudarlo a construir sus propias restricciones.