UILabel sizeToFit no funciona con el autolayout ios6
xcode controls list (13)
Aunque la pregunta se establece de forma programática, después de haber encontrado el mismo problema y de preferir trabajar en Interface Builder, pensé que podría ser útil agregar a las respuestas existentes una solución de Interface Builder.
Lo primero es olvidar sizeToFit
. Auto Layout se encargará de esto en su nombre en función del tamaño del contenido intrínseco.
El problema, por lo tanto, es cómo hacer que una etiqueta se ajuste a su contenido con Diseño automático. Específicamente, porque la pregunta lo menciona, la altura. Tenga en cuenta que los mismos principios se aplican al ancho.
Comencemos con un ejemplo de UILabel que tiene una altura establecida en 41px de alto:
Como puede ver en la captura de pantalla de arriba, "This is my text"
tiene relleno arriba y abajo. Eso es relleno entre la altura de UILabel y su contenido, el texto.
Si ejecutamos la aplicación en el simulador, efectivamente, vemos lo mismo:
Ahora, seleccionemos UILabel en Interface Builder, y observemos la configuración predeterminada en el inspector de Tamaño:
Tenga en cuenta la restricción resaltada arriba. Esa es la prioridad de abrazar el contenido . Como lo describe Erica Sadun en el excelente iOS Auto Layout Demystified , esto es:
la forma en que una vista prefiere evitar un relleno extra alrededor de su contenido central
Para nosotros, con UILabel, el contenido central es el texto.
Aquí llegamos al corazón de este escenario básico. Le hemos dado a nuestra etiqueta de texto dos restricciones. Ellos entran en conflicto. Uno dice "la altura debe ser igual a 41 píxeles de alto" . El otro dice "abrace la vista de su contenido para que no tengamos ningún relleno adicional" . En nuestro caso, abrace la vista de su texto para que no tengamos ningún relleno extra.
Ahora, con Diseño automático, con dos instrucciones diferentes que dicen hacer cosas diferentes, el tiempo de ejecución tiene que elegir una u otra. No puede hacer ambas cosas. UILabel no puede tener 41 píxeles de alto ni tener relleno.
La forma en que esto se resuelve es especificando la prioridad. Una instrucción tiene que tener una prioridad más alta que la otra. Si ambas instrucciones dicen cosas diferentes y tienen la misma prioridad, se producirá una excepción.
Así que demos una oportunidad. Mi restricción de altura tiene una prioridad de 1000 , que es obligatoria . La altura de abrazo de contenido es 250 , que es débil . ¿Qué sucede si reducimos la prioridad de restricción de altura a 249 ?
Ahora podemos ver que la magia comienza a suceder. Probemos en el sim:
¡Increíble! Contenido abrazando logrado. Solo porque la prioridad de altura 249 es menor que la prioridad de aceptación de contenido 250 . Básicamente, estoy diciendo que "la altura que especifico aquí es menos importante que lo que he especificado para el abrazo de contenido" . Por lo tanto, el contenido abrazando gana.
En pocas palabras, lograr que la etiqueta se ajuste al texto puede ser tan simple como especificar la restricción de altura o ancho, y corregir la configuración de esa prioridad en asociación con la restricción de prioridad de abrazos de contenido de ese eje.
Saldrá haciendo el equivalente de ancho como un ejercicio para el lector!
¿Cómo se supone que configure programáticamente (y en qué método) un UILabel cuya altura depende de su texto? He intentado configurarlo usando una combinación de Guión gráfico y código, pero fue en vano. Todos recomiendan sizeToFit
al configurar lineBreakMode
y numberOfLines
. Sin embargo, no importa si puse ese código en viewDidLoad:
viewDidAppear:
o viewDidLayoutSubviews
no puedo hacer que funcione. O hago que la caja sea demasiado pequeña para texto largo y no crece, o la hago demasiado grande y no se encoge.
En iOS 6, usando el autolayout, si se fijan los lados (o ancho) y la parte superior de UILabel, crecerá y contraerá automáticamente para ajustarse a su contenido, sin ningún código y sin interferir con su resistencia a la compresión o lo que sea. Es muy simple.
En casos más complejos, simplemente configure el valor de MaxLayoutWidth preferredMaxLayoutWidth
la etiqueta.
De cualquier manera, lo correcto sucede automáticamente.
En mi caso, al usar las etiquetas en una UITableViewCell, la etiqueta en cambiaría el tamaño, pero la altura sobrepasaría la altura de la celda de la tabla. Esto es lo que funcionó para mí. Lo hice de acuerdo con Max MacLeod, y luego me aseguré de que la altura de la celda estuviera configurada en UITableViewAutomaticDimension.
Puedes agregar que está en tu init o awakeFromNib,
self.tableView.rowHeight = UITableViewAutomaticDimension.
En el guión gráfico, seleccione la celda, abra el inspector de tamaño y asegúrese de que la altura de la fila esté configurada en "Predeterminado" tocando la casilla "Personalizar".
Hay un problema en 8.0 que también requiere que se establezca en el código.
En mi caso, estaba creando una subclase UIView que contenía un UILabel (de longitud desconocida). En iOS7 el código era sencillo: establezca las restricciones, no se preocupe por el contenido abrazado o la resistencia a la compresión, y todo funcionó como se esperaba.
Pero en iOS6 el UILabel siempre estaba recortado en una sola línea. Ninguna de las respuestas anteriores funcionó para mí. Se ignoraron los ajustes de abrazo de contenido y resistencia a la compresión. La única solución que impedía el recorte era incluir un MaxLayoutWidth preferido en la etiqueta. Pero no sabía a qué establecer el ancho preferido, ya que el tamaño de su vista principal era desconocido (de hecho, estaría definido por los contenidos).
Finalmente encontré la solución here . Como estaba trabajando en una vista personalizada, solo podía agregar el siguiente método para establecer el ancho de diseño preferido una vez que las restricciones se habían calculado una vez, y luego volver a calcularlas:
- (void)layoutSubviews
{
// Autolayout hack required for iOS6
[super layoutSubviews];
self.bodyLabel.preferredMaxLayoutWidth = self.bodyLabel.frame.size.width;
[super layoutSubviews];
}
He resuelto con xCode6 poner "Ancho Preferido" en Automático y fijar la etiqueta arriba, adelante y atrás
Me encontré con este problema también con una subclase UIView que contiene un UILabel como uno si sus elementos internos. Incluso con el diseño automático y probando todas las soluciones recomendadas, la etiqueta simplemente no ajustaría su altura al texto. En mi caso, solo quiero que la etiqueta sea una línea.
De todos modos, lo que funcionó para mí fue agregar una restricción de altura requerida para el UILabel y establecerlo manualmente a la altura correcta cuando se llama a intrinsicContentSize. Si no tiene el UILabel incluido en otro UIView, puede intentar subclasificar UILabel y proporcionar una implementación similar al establecer primero la restricción de altura y luego regresar
[super instrinsicContentSize]; en lugar de [self.containerview intrinsiceContentSize]; como hago abajo, que es específico de mi subclase UIView.
- (CGSize)intrinsicContentSize
{
CGRect expectedTextBounds = [self.titleLabel textRectForBounds:self.titleLabel.bounds limitedToNumberOfLines:1];
self.titleLabelHeightConstraint.constant = expectedTextBounds.size.height;
return [self.containerView intrinsicContentSize];
}
Funciona perfectamente ahora en iOS 7 e iOS 8.
Notado en IOS7 sizeToFit no funcionaba también - quizás la solución también podría ayudarlo
[textView sizeToFit];
[textView layoutIfNeeded];
Otra opción para garantizar que el valor deMaxLayoutWidth preferido de la etiqueta se mantenga sincronizado con el ancho de la etiqueta:
#import "MyLabel.h"
@implementation MyLabel
-(void)setBounds:(CGRect)bounds
{
[super setBounds:bounds];
// This appears to be needed for iOS 6 which doesn''t seem to keep
// label preferredMaxLayoutWidth in sync with its width, which
// means the label won''t grow vertically to encompass its text if
// the label''s width constraint changes.
self.preferredMaxLayoutWidth = self.bounds.size.width;
}
@end
Siento que debo contribuir ya que me tomó un tiempo encontrar la solución correcta:
- El objetivo es dejar que Auto Layout haga su trabajo sin llamar a sizeToFit (), lo haremos especificando las restricciones correctas:
- Especifique las restricciones de espacio superior, inferior y principal / final en su UILabel
- Establezca el número de líneas propiedad en 0
- Incremente la prioridad de abrazar contenido a 1000
- Baje la prioridad de resistencia a la compresión de contenido a 500
- En la restricción del contenedor inferior, disminuya la prioridad a 500
Básicamente, lo que pasa es que le dices a tu UILabel que, aunque tiene una restricción de altura fija, puede hacer que la restricción sea más pequeña para abrazar el contenido (si tienes una sola línea, por ejemplo), pero no puede rompe la restricción para hacerlo más grande.
UILabel
programáticamente y en mi caso eso fue suficiente:
label.translatesAutoresizingMaskIntoConstraints = false
label.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: .Vertical)
label.numberOfLines = 0
Tenga en cuenta que en la mayoría de los casos la solución de Matt funciona como se esperaba. Pero si no funciona para usted, por favor, lea más.
Para que su etiqueta cambie automáticamente el tamaño de la altura, debe hacer lo siguiente:
- Establecer restricciones de diseño para la etiqueta
- Establecer la restricción de altura con baja prioridad. Debería ser menor que ContentCompressionResistancePriority
- Establecer numberOfLines = 0
- Establecer ContentHuggingPriority mayor que la prioridad de altura de la etiqueta
- Establezca preferredMaxLayoutWidth para la etiqueta. Ese valor es utilizado por la etiqueta para calcular su altura
Por ejemplo:
self.descriptionLabel = [[UILabel alloc] init];
self.descriptionLabel.numberOfLines = 0;
self.descriptionLabel.lineBreakMode = NSLineBreakByWordWrapping;
self.descriptionLabel.preferredMaxLayoutWidth = 200;
[self.descriptionLabel setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.descriptionLabel setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
[self.descriptionLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:self.descriptionLabel];
NSArray* constrs = [NSLayoutConstraint constraintsWithVisualFormat:@"|-8-[descriptionLabel_]-8-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)];
[self addConstraints:constrs];
[self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-8-[descriptionLabel_]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)]];
[self.descriptionLabel addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[descriptionLabel_(220@300)]" options:0 metrics:nil views:NSDictionaryOfVariableBindings(descriptionLabel_)]];
Usando Interface Builder
Configure cuatro restricciones. La restricción de altura es obligatoria.
Luego vaya al inspector de atributos de la etiqueta y establezca el número de líneas en 0.
Diríjase al inspector de tamaño de la etiqueta y aumente la Prioridad vertical de contenido vertical y la Prioridad vertical de compresión de contenido vertical.
Seleccione y edite la restricción de altura.
Y disminuya la prioridad de restricción de altura.
Disfrutar. :)
UIFont *customFont = myLabel.font;
CGSize size = [trackerStr sizeWithFont:customFont
constrainedToSize:myLabel.frame.size // the size here should be the maximum size you want give to the label
lineBreakMode:UILineBreakModeWordWrap];
float numberOfLines = size.height / customFont.lineHeight;
myLabel.numberOfLines = numberOfLines;
myLabel.frame = CGRectMake(258, 18, 224, (numberOfLines * customFont.lineHeight));