Múltiples UILabels dentro de un auto dimensionamiento UITableViewCell


En esta aplicación de iOS 8 que estoy creando, tengo una vista de tabla y necesito que se redimensionen. Lo implementé usando Auto Layout y funciona. Casi. Así es como se ve ahora.

introduzca la descripción de la imagen aquí

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 redimensionada sí mismo correctamente sin ningún problema 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 se redimensiona para que se ajuste a todo el texto.

Ahora está pasando algo raro. Le doy la vuelta al paisaje y aquí está.

introduzca la descripción de la imagen aquí

Dado que hay espacio, la etiqueta muestra todo el texto. Fino. Luego lo vuelvo a retrato.

introduzca la descripción de la imagen aquí

Ahora la etiqueta pequeña se ha redimensionado a ajuste todo su texto, pero se desborda los límites de las celdas. Intenté hacer la célula más grande pero no funcionó. Dado que se trata de células de auto dimensionamiento, no creo que sea la manera correcta incluso.

Tampoco recibo ningún error ni advertencia en mis restricciones de diseño automático.

introduzca la descripción de la imagen aquí

He establecido estas dos líneas de código en el método viewDidLoad().

tableView.estimatedRowHeight = 100
tableView.rowHeight = UITableViewAutomaticDimension

¿Puede alguien por favor decirme lo que podría estar haciendo mal aquí?

Ya que es difícil responder con solo mirar imágenes y no tengo más código para publicar al lado del fragmento de código anterior, subí un proyecto Xcode ejecutable que demuestra el problema aquí. (Hay 2 celdas personalizadas. Básicamente es la misma celda solo la altura se incrementa en la segunda.)

He estado jugando con las restricciones de diseño automático, pero parece que no puedo conseguir que esto funcione. Cualquier ayuda sería apreciada.

Gracias.


ACTUALIZACIÓN:

Con la ayuda de este tutorial I encontré algunos consejos útiles. De acuerdo con él, cada subview debe tener restricciones que anclan 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 post original, tenía espacios verticales entre cada etiqueta, así que creo que esa es la razón por la que auto layout no pudo calcular la altura adecuada.

Así que hice algunos cambios.

  • Reduje el espacio vertical entre las etiquetas a 0 y establecí la Vertical restricciones de espacio entre las etiquetas superiores e intermedias y las etiquetas medias e inferiores.
  • He añadido restricciones iniciales, superiores y finales a la etiqueta superior.
  • Al principio y al final de la etiqueta central.
  • Delante, abajo, detrás de la etiqueta inferior.

Ahora aquí hay otra parte extraña. Cuando lo ejecuté por primera vez, el problema de recorte de la etiqueta inferior sigue ahí.

introduzca la descripción de la imagen aquí

Pero si giro el dispositivo a horizontal y lo vuelvo a vertical, todos los todos los las celdas se cambian de tamaño correctamente para adaptarse a ambas etiquetas!

introduzca la descripción de la imagen aquí

Todavía no puedo entender por qué esto no sucede al principio, sin embargo. El proyecto Xcode actualizado es aquí.

Author: Isuru, 2014-09-20

5 answers

El problema aquí es con las etiquetas multilínea' preferredMaxLayoutWidth propiedad. Esta es la propiedad que indica a la etiqueta cuándo debe ajustar la línea. Debe configurarse correctamente para que cada etiqueta intrinsicContentSize tenga la altura correcta, que es en última instancia lo que Auto Layout usará 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) donde esto no se establece correctamente/automáticamente al cargar una celda desde un plumín o guion gráfico.

Para evitar este error, necesitamos tener el preferredMaxLayoutWidth configurado para ser exactamente igual al ancho final de la etiqueta una vez que se muestre 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 simplemente 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 utilizando el ancho de cada etiqueta para establecer el preferredMaxLayoutWidth however sin embargo, si comprueba el ancho de las 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 establecido sus subviews.

¿Cómo conseguimos 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 junte:

// 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)

OK, entonces, ¿qué estamos haciendo aquí? Bueno, te darás cuenta de que hay 3 nuevas líneas de código añadidas. Primero, necesitamos establecer el ancho de esta celda de vista de tabla para que coincida con el ancho real de la vista de tabla (esto asume que la vista de tabla ya se ha diseñado y tiene su ancho final, que debería ser el caso). Efectivamente estamos haciendo que el ancho de la celda sea correcto temprano, ya que la vista de tabla lo hará eventualmente.

También notarás que estamos usando 99999 para la altura. ¿De qué va eso? Esa es una solución simple para el problema discutido en detalle aquí, donde si sus restricciones requieren más espacio vertical que la altura actual del contentView de la celda, obtiene una excepción de restricción que en realidad no indica ningún problema real. La altura de la celda o cualquiera de sus subviews en realidad no importa en este punto, 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 misma, estableciendo los límites del contentView para igualar 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. Simplemente configurando 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, que hará que el motor de diseño automático resuelva sus restricciones y actualice los marcos de todas las subviews. Ya 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 etiqueta también serán correctos, lo que significa que el preferredMaxLayoutWidth establecido en 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.

Este es definitivamente un error de Apple en UIKit que tenemos que solucionar por ahora (así que por favor haga envíe informes de errores con Apple para que priorizar una solución!).

Una nota final: esta solución se encontrará con problemas si el ancho de la celda de la vista de tabla contentView no extiende el ancho completo de la vista de tabla, por ejemplo, cuando hay un índice de sección que se muestra a la derecha. En este caso, deberá asegurarse de tener esto en cuenta manualmente al establecer el ancho de la celda may 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)
 79
Author: smileyborg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-06 16:01:55

Me encontré con el mismo problema que usted 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, puse dos etiquetas en vertical, ambas son de altura dinámica. La altura de la celda está configurada correctamente para contener las dos etiquetas.

 48
Author: fogisland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-11-08 02:46:29

Asumiendo que no tiene ningún error con sus restricciones como otros han sugerido, este problema parece provenir del uso de una etiqueta UILabel que permite múltiples líneas en conjunción con un UITableViewCellAccessory. Cuando iOS establece 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 obtiene truncamiento donde no esperaría.

Asumiendo que desea que la etiqueta UILabel extienda todo el ancho de la vista de contenido, Escribí un método que arregla 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 vaya a tener varias 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 smileyborg había mencionado, si no estaba utilizando el ancho completo del contentView, podría hacer referencia a las restricciones y restarlas del ancho como bien.

Editar: Anteriormente estaba ejecutando 'layoutIfNeeded' en la celda, pero esto estaba creando problemas de rendimiento y no parecía ser necesario de todos modos. Quitarlo no me ha causado ningún problema.

 6
Author: user2898617,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-28 16:46:04

Tienes dos problemas aquí.

1 / Ahora mismo, usando el Lenguaje de Formato Visual, las restricciones verticales de su celda se pueden traducir de la siguiente 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 inicia su aplicación. Luego, cuando rotas tu dispositivo al modo horizontal, actionsLabel acepta que se muestre con dos líneas o más. Luego, cuando gira su dispositivo de nuevo al modo retrato, 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 superponen los límites de su celda.

Si desea resolver todos esos problemas, primero le recomiendo que reconstruya su archivo xib desde cero. Esto curará su comportamiento extraño actionsLabel (dos líneas o más solo cuando gire el dispositivo).

Entonces, usted tiene 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). De la misma manera, su margen inferior se puede establecer en otro valor que -(10)-.

Adición

Con el fin de responder a su pregunta, he creado un proyecto simple con el patrón de restricciones anteriores en mi .archivo xib. La siguiente imagen puede ayudarte a crear tus propias restricciones.

introduzca la descripción de la imagen aquí

 4
Author: Imanou Petit,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-20 19:02:37

Probé la solución muy fácil y elegante de "fogisland", y no funcionó. Afortunadamente descubrí que una línea adicional hace que funcione en todas las direcciones. Sólo decirle al sistema que no sólo sugieren un nuevo diseño (layoutIfNeeded), que pedirlo explícitamente (setNeedLayout)

    cell.setNeedsLayout()
    cell.layoutIfNeeded()
    return cell
 1
Author: Hardy_Germany,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-16 20:05:34