iOS: UILabel multilínea en diseño automático

183

Tengo problemas para tratar de lograr un comportamiento de diseño muy básico con Auto Layout. Mi controlador de vista se ve así en IB:

ingrese la descripción de la imagen aquí

La etiqueta superior es la etiqueta del título, no sé cuántas líneas será. Necesito la etiqueta del título para mostrar todas las líneas de texto. También necesito que las otras dos etiquetas y la pequeña imagen se coloquen justo debajo del título, sin importar cuán alto sea. He establecido restricciones de espacio vertical entre las etiquetas y la imagen pequeña, así como una restricción de espacio superior entre la etiqueta del título y su supervista y una restricción de espacio inferior entre la imagen pequeña y su supervista. La UIView blanca no tiene restricción de altura, por lo que debe estirarse verticalmente para contener sus subvistas. He establecido el número de líneas para la etiqueta del título en 0.

¿Cómo puedo hacer que la etiqueta del título cambie de tamaño para ajustarse al número de líneas requeridas por la cadena? Entiendo que no puedo usar los métodos setFrame porque estoy usando Auto Layout. Y tengo que usar el diseño automático porque necesito esas otras vistas para permanecer debajo de la etiqueta del título (de ahí las restricciones).

¿Cómo puedo hacer que esto suceda?

James Harpe
fuente
3
También estoy luchando con un problema similar. Pero todavía estoy luchando para que la etiqueta superior ajuste su altura dinámicamente para que se ajuste al contenido. ¿Cómo lo lograste?
jbandi
1
Considere marcar la respuesta de @ mwhuss como la aceptada.
jemmons
1
¿Logró el resultado requerido?
Aniruddha
verifique esto, no necesita agregar una sola línea de código stackoverflow.com/a/36862795/4910767
Badal Shah

Respuestas:

254

El uso -setPreferredMaxLayoutWidthen UILabely autolayout debería manejar el resto.

[label setPreferredMaxLayoutWidth:200.0];

Consulte la documentación de UILabel en preferredMaxLayoutWidth .

Actualizar:

Solo es necesario establecer la heightrestricción en el guión gráfico a Greater than or equal to, no es necesario establecerPreferredMaxLayoutWidth.

mwhuss
fuente
8
Buena, de todos modos para hacer esto en el generador de interfaces?
Sjoerd realiza el
12
Establezca
10
Recordatorio: recuerde establecer la propiedad numberOfLines.
Li Fumin
2
Sí, use lo que quiera que sea el ancho máximo.
mwhuss
44
Consulte stackoverflow.com/a/29180323/1529675 para saber cómo determinar preferredMaxLayoutWidthy ver devetc.org/code/2014/07/07/auto-layout-and-views-that-wrap.html para una breve pero informativa redacción, completa con GIF animados (no es mi escrito, lo encontré a través de Google)
tboyce12
213

Expanda el número de líneas del conjunto de etiquetas a 0 y, lo que es más importante, para la altura del conjunto de diseño automático a> = x. El diseño automático hará el resto. También puede contener sus otros elementos basados ​​en el elemento anterior para posicionarlos correctamente.

diseño automático

Charlie Wu
fuente
Si este método no funciona para usted, consulte la respuesta de Cory Imdieke sobre asegurarse de que las vistas posteriores no prohíban que el Diseño automático cambie el tamaño de la (posiblemente) vista multilínea.
Tom Howard
1
Esta es la única respuesta que funcionó para mí dentro de una celda de vista de tabla. También funciona con un número fijo de filas (en lugar de 0 / ilimitado)
bgolson
Esto no funcionó para mí con un conjunto de ancho máximo preferido explícito, así que asegúrese de que esté configurado en automático en IB cuando use esta técnica. Saludos
jmc
La configuración del ancho máximo automático solo está disponible en iOS8. También, según tengo entendido, ajustará la altura automáticamente y, en mi experiencia, no es necesario establecer una restricción de altura para que funcione. Esto funcionó para mí usando un ancho explícito.
Tony
1
Esto no funcionó para mí. Creo que stackoverflow.com/a/13032251/1529675 es la respuesta correcta.
tboyce12
48

Fuente: http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html

Tamaño del contenido intrínseco del texto de varias líneas

El tamaño del contenido intrínseco de UILabel y NSTextField es ambiguo para texto de varias líneas. La altura del texto depende del ancho de las líneas, que aún no se ha determinado al resolver las restricciones. Para resolver este problema, ambas clases tienen una nueva propiedad llamada preferredMaxLayoutWidth, que especifica el ancho de línea máximo para calcular el tamaño del contenido intrínseco.

Como generalmente no conocemos este valor de antemano, debemos adoptar un enfoque de dos pasos para hacerlo bien. Primero dejamos que el diseño automático haga su trabajo, y luego usamos el marco resultante en el pase de diseño para actualizar el ancho máximo preferido y activar el diseño nuevamente.

- (void)layoutSubviews
{
    [super layoutSubviews];
    myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
    [super layoutSubviews];
}

La primera llamada a [super layoutSubviews] es necesaria para que la etiqueta obtenga su conjunto de marcos, mientras que la segunda llamada es necesaria para actualizar el diseño después del cambio. Si omitimos la segunda llamada, obtenemos un error NSInternalInconsistencyException, porque hemos realizado cambios en el pase de diseño que requieren actualizar las restricciones, pero no activamos el diseño nuevamente.

También podemos hacer esto en una subclase de etiqueta:

@implementation MyLabel
- (void)layoutSubviews
{
    self.preferredMaxLayoutWidth = self.frame.size.width;
    [super layoutSubviews];
}
@end

En este caso, no necesitamos llamar a [super layoutSubviews] primero, porque cuando se llama a layoutSubviews, ya tenemos un marco en la etiqueta.

Para realizar este ajuste desde el nivel del controlador de vista, conectamos a viewDidLayoutSubviews. En este punto, los fotogramas de la primera pasada de diseño automático ya están configurados y podemos usarlos para establecer el ancho máximo preferido.

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    myLabel.preferredMaxLayoutWidth = myLabel.frame.size.width;
    [self.view layoutIfNeeded];
}

Por último, asegúrese de no tener una restricción de altura explícita en la etiqueta que tenga una prioridad más alta que la prioridad de resistencia de compresión de contenido de la etiqueta. De lo contrario, prevalecerá sobre la altura calculada del contenido. Asegúrese de verificar todas las restricciones que pueden afectar la altura de la etiqueta.

Anton Matosov
fuente
gracias gracias gracias no solo por el código, sino más importante aún por la explicación y los ejemplos de cómo hacer esto no solo en una subclase, sino también desde el controlador de vista.
djibouti33
Estoy intentando esto en mi controlador de vista, y se llama a viewDidLayoutSubviews después de viewWillAppear y viewDidAppear. Después de viewDidAppear, hay un destello cuando las etiquetas cambian de tamaño correctamente. ¿Hay alguna manera de evitar esto? ¿Hay alguna manera de completar el cambio de tamaño antes de que el usuario vea algo? en mi caso, mis etiquetas de varias líneas están en tableHeaderView, por lo que también cambio el tamaño de la altura de la vista del encabezado según las etiquetas con este ejemplo ( stackoverflow.com/questions/20982558/… )
djibouti33
@ djibouti33 ¿puede proporcionar el código de muestra mínimo (por ejemplo, en formato de repositorio github) para que pueda verificar fácilmente su problema?
Anton Matosov
Gracias @ Anton. Nuestro diseño ha cambiado desde un UITableView a un UICollectionView, y afortunadamente no vi este comportamiento. Si puedo acceder a mi historial de git para crear un pequeño proyecto de muestra, te lo haré saber.
djibouti33
Un problema para mí desde que comencé a ejecutar en un simulador de iOS 9, pero cuando probé en iOS 8, comenzó el problema. Necesito establecer el ancho máximo manualmente.
naz
17

Estaba luchando con este escenario exacto, pero con bastantes vistas más que debían cambiar de tamaño y bajar según fuera necesario. Me estaba volviendo loco, pero finalmente lo descubrí.

Aquí está la clave: a Interface Builder le gusta agregar restricciones adicionales a medida que agrega y mueve vistas y es posible que no lo note. En mi caso, tenía una vista a mitad de camino que tenía una restricción adicional que especificaba el tamaño entre ella y su supervista, básicamente fijándola en ese punto. Eso significaba que nada por encima podría cambiar su tamaño porque iría en contra de esa restricción.

Una manera fácil de saber si este es el caso es tratando de cambiar el tamaño de la etiqueta manualmente. ¿IB te deja crecer? Si es así, ¿se mueven las etiquetas a continuación como esperabas? Asegúrese de haber marcado ambos antes de cambiar el tamaño para ver cómo sus restricciones moverán sus vistas:

Menú IB

Si la vista está atascada, siga las vistas que están debajo y asegúrese de que una de ellas no tenga un espacio superior para supervisar la restricción. Luego, solo asegúrese de que su opción de número de líneas para la etiqueta esté establecida en 0 y que se haga cargo del resto.

Cory Imdieke
fuente
Sí, después de mucho jugar con él pude obtener el efecto que quería. Solo tiene que pensar detenidamente sobre las restricciones que desea. No ayuda que IB esté constantemente imponiendo restricciones que no esperas.
James Harpe
5

Creo que necesitas lo siguiente:

  • Una restricción superior
  • Una restricción principal (por ejemplo, lado izquierdo)
  • Una restricción final (por ejemplo, lado derecho)
  • Establezca la prioridad de abrazo de contenido, horizontal a bajo, de modo que llene el espacio dado si el texto es corto.
  • Establezca la resistencia de compresión del contenido, horizontal a baja, para que se ajuste en lugar de intentar ensancharse.
  • Establezca el número de líneas en 0.
  • Establezca el modo de salto de línea en ajuste de línea.
Chris
fuente
Esto debería funcionar en xcode9. No tuve que hacer nada con la restricción de altura de la etiqueta (no tengo ninguna): solo funciona fuera de la caja últimamente una vez que la etiqueta se haya restringido (final, inicial, superior e inferior). uikit hace el resto
Anton Tropashko
El contenido que abarcaba la prioridad era mi problema. Gracias por hacerme consciente de esa propiedad, así como de la resistencia a la compresión. Me ayudaste a resolver un problema de una hora.
David Gay
0

Ninguna de las diferentes soluciones encontradas en los muchos temas sobre el tema funcionó perfectamente para mi caso (x etiquetas dinámicas multilínea en celdas de vista de tabla dinámica).

Encontré una manera de hacerlo:

Después de haber establecido las restricciones en su etiqueta y establecer su propiedad multilínea en 0, cree una subclase de UILabel; Llamé a la mía AutoLayoutLabel:

@implementation AutoLayoutLabel

- (void)layoutSubviews{
    [self setNeedsUpdateConstraints];
    [super layoutSubviews];
    self.preferredMaxLayoutWidth = CGRectGetWidth(self.bounds);
}

@end
Tanguy G.
fuente
0

Tengo un UITableViewCell que tiene una etiqueta de ajuste de texto. Trabajé el ajuste de texto de la siguiente manera.

1) Establezca las restricciones de UILabel de la siguiente manera.

ingrese la descripción de la imagen aquí

2) Establecer no. de líneas a 0.

3) Se agregó la restricción de altura UILabel a UITableViewCell.

@IBOutlet weak var priorityLabelWidth: NSLayoutConstraint!

4) En UITableViewCell:

priorityLabel.sizeToFit()
priorityLabelWidth.constant = priorityLabel.intrinsicContentSize().width+5
AG
fuente
-12

Una forma de hacer esto ... A medida que aumenta la longitud del texto, intente cambiar (disminuir) el tamaño de fuente del texto de la etiqueta usando

Label.adjustsFontSizeToFitWidth = YES;
usuario1662392
fuente
1
Quiero que las diferentes instancias del controlador de vista tengan una apariencia coherente, por lo que no quiero cambiar el tamaño de fuente.
James Harpe