web-dev-qa-db-fra.com

UILabel sizeToFit ne fonctionne pas avec autolayout ios6

Comment suis-je censé configurer par programme (et selon quelle méthode) un UILabel dont la hauteur dépend de son texte? J'ai essayé de le configurer à l'aide d'une combinaison de Storyboard et de code, mais en vain. Tout le monde recommande sizeToFit en réglant lineBreakMode et numberOfLines. Cependant, peu importe si j'ai mis ce code dans viewDidLoad:, viewDidAppear: ou viewDidLayoutSubviews, je ne peux pas le faire fonctionner. Soit je réduis la boîte trop petite pour un texte long et elle ne grossit pas, soit je la fais trop grosse et elle ne se réduit pas.

148
circuitlego

Veuillez noter que dans la plupart des cas la solution de Matt fonctionne comme prévu. Mais si cela ne fonctionne pas pour vous, s'il vous plaît, lisez plus loin.

Pour que votre étiquette redimensionne automatiquement la hauteur, procédez comme suit:

  1. Définir des contraintes de mise en page pour l'étiquette 
  2. Définissez la contrainte de hauteur avec une priorité basse. Il devrait être inférieur à ContentCompressionResistancePriority
  3. Définir numberOfLines = 0
  4. Définir ContentHuggingPriority supérieur à la priorité de hauteur de l'étiquette
  5. Définissez preferredMaxLayoutWidth pour label. Cette valeur est utilisée par label pour calculer sa hauteur.

Par exemple:

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_)]];

Utilisation d'Interface Builder

  1. Définissez quatre contraintes. La contrainte de hauteur est obligatoire .enter image description here

  2. Accédez ensuite à l'inspecteur d'attributs de l'étiquette et définissez le nombre de lignes sur 0 .enter image description here

  3. Accédez à l'inspecteur de taille de l'étiquette et augmentez les propriétés vertical ContentHuggingPriority et vertical ContentCompressionResistancePriority.
    enter image description here

  4. Sélectionnez et modifiez la contrainte de hauteur.
    enter image description here

  5. Et diminuez la priorité de contrainte de hauteur.
    enter image description here

Prendre plaisir. :)

400
Mark Kryzhanouski

Sous iOS 6, en utilisant autolayout, si les côtés (ou la largeur) et le haut d’un UILabel sont épinglés, il se lève automatiquement grossit et se rétrécit verticalement pour s’ajuster à son contenu, avec pas de code du tout = et pas de problème avec sa résistance à la compression ou quoi que. C'est simple comme bonjour.

Dans des cas plus complexes, il suffit de définir le preferredMaxLayoutWidth de l'étiquette.

Quoi qu'il en soit, la bonne chose se produit automatiquement.

65
matt

Bien que la question dise par programmation, après avoir rencontré le même problème et préférant travailler dans Interface Builder, j'ai pensé qu'il pourrait être utile d'ajouter aux réponses existantes une solution Interface Builder. 

La première chose à faire est d’oublier sizeToFit. Auto Layout se chargera de cela en votre nom en fonction de la taille du contenu intrinsèque. 

Le problème est donc de savoir comment obtenir une étiquette qui s'adapte à son contenu avec la mise en page automatique. Plus précisément - parce que la question le mentionne - la hauteur. Notez que les mêmes principes s'appliquent à la largeur.

Commençons donc avec un exemple UILabel dont la hauteur est fixée à 41 pixels de haut:

enter image description here

Comme vous pouvez le voir dans la capture d'écran ci-dessus, "This is my text" a un remplissage en haut et en bas. C'est un padding entre la hauteur de UILabel et son contenu, le texte.

Si nous exécutons l'application dans le simulateur, bien sûr, nous verrons la même chose:

enter image description here

Maintenant, sélectionnons UILabel dans Interface Builder et examinons les paramètres par défaut dans l'inspecteur de taille:

enter image description here

Notez la contrainte en surbrillance ci-dessus. C’est la priorité Content Hugging. Comme Erica Sadun le décrit dans l'excellent iOS Auto Layout Demystified , ceci est:

la façon dont une vue préfère éviter un remplissage supplémentaire autour de son contenu principal

Pour nous, avec UILabel, le contenu principal est le texte.

Nous arrivons ici au cœur de ce scénario de base. Nous avons attribué à notre libellé deux contraintes. Ils sont en conflit. On dit "la hauteur doit être égale à 41 pixels". L'autre dit ("étreint la vue avec son contenu afin que nous n'ayons pas de remplissage supplémentaire". Dans notre cas, calez la vue sur texte afin que nous n'ayons aucun remplissage supplémentaire.

Maintenant, avec la mise en page automatique, avec deux instructions différentes qui disent de faire des choses différentes, le moteur d’exécution doit choisir l’une ou l’autre. Il ne peut pas faire les deux. UILabel ne peut pas avoir une hauteur de 41 pixels, et n'ayant pas de remplissage. 

La façon dont cela est résolu est en spécifiant la priorité. Une instruction doit avoir une priorité plus élevée que l'autre. Si les deux instructions disent des choses différentes et ont la même priorité, une exception se produira.

Alors allons-y. Ma contrainte de hauteur a une priorité de 1000, qui est required. La hauteur du contenu est 250, soit faible. Que se passe-t-il si nous réduisons la priorité de contrainte de hauteur à 249?

enter image description here

Nous pouvons maintenant voir que la magie commence à se produire. Essayons dans la sim:

enter image description here

Impressionnant! Contenu étreignant atteint. Seulement parce que la priorité d'altitude 249 est inférieure à la priorité d'accolade au contenu 250. En gros, je dis "la hauteur que je spécifie ici est moins importante que ce que j'ai spécifié pour le contenu". Ainsi, le contenu embrassant gagne.

Pour que l'étiquette corresponde parfaitement au texte, il suffit de spécifier la contrainte de hauteur ou de largeur et de définir correctement cette priorité en association avec la contrainte de priorité de verrouillage du contenu de cet axe. 

Laissera faire l'équivalent pour la largeur comme un exercice pour le lecteur!

40
Max MacLeod

Remarqué dans IOS7 sizeToFit ne fonctionnait pas aussi - peut-être que la solution peut aussi vous aider

[textView sizeToFit];
[textView layoutIfNeeded];
7
Nick Wood

Je pense que je devrais contribuer car cela m'a pris du temps pour trouver la bonne solution:

  • L'objectif est de laisser Auto Layout faire son travail sans jamais appeler sizeToFit (), nous le ferons en spécifiant les bonnes contraintes:
  • Spécifiez les contraintes d'espace haut, bas et avant/arrière sur votre UILabel
  • Définissez la propriété number of lines sur 0
  • Incrémenter la priorité d'accolade du contenu à 1000
  • Abaissez la priorité de résistance à la compression du contenu à 500
  • Sur votre contrainte de conteneur inférieure, réduisez la priorité à 500

En gros, vous dites à votre UILabel que même s'il a une contrainte de hauteur fixe, il peut casser la contrainte pour se réduire afin de caler le contenu (si vous avez une seule ligne par exemple), mais il ne peut pas briser la contrainte pour l'agrandir.

4
ArturT

Une autre option permettant de s'assurer que la propriété preferredMaxLayoutWidth de l'étiquette est maintenue en phase avec la largeur de l'étiquette:

#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
4
Monte Hurd

Dans mon cas, je créais une sous-classe UIView contenant un UILabel (de longueur inconnue). Dans iOS7, le code était simple: définissez les contraintes, ne vous inquiétez pas du contenu ni de la résistance à la compression, et tout a fonctionné comme prévu. 

Mais dans iOS6, UILabel était toujours coupé sur une seule ligne. Aucune des réponses ci-dessus n'a fonctionné pour moi. Les paramètres d'étirement du contenu et de résistance à la compression ont été ignorés. La seule solution qui empêchait l'écrêtage consistait à inclure une couleur preferredMaxLayoutWidth sur l'étiquette. Mais je ne savais pas à quoi définir la largeur préférée, car la taille de la vue parent était inconnue (en fait, elle serait définie par le contenu). 

J'ai finalement trouvé la solution ici . Comme je travaillais sur une vue personnalisée, je pouvais simplement ajouter la méthode suivante pour définir la largeur de présentation préférée après le calcul des contraintes, puis les recalculer:

- (void)layoutSubviews
{
    // Autolayout hack required for iOS6
    [super layoutSubviews];
    self.bodyLabel.preferredMaxLayoutWidth = self.bodyLabel.frame.size.width;
    [super layoutSubviews];
}
3
sumizome

J'ai ajouté UILabel par programmation et dans mon cas, cela suffisait:

label.translatesAutoresizingMaskIntoConstraints = false
label.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: .Vertical)
label.numberOfLines = 0
2
mixel

j'ai résolu avec xCode6 en mettant "Largeur recommandée" sur Automatique et en pinçant le haut de l'étiquette, en tête et en fin enter image description here

2
Kazzar
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));
0
Swati

Une solution qui a fonctionné pour moi Si votre UILabel a une largeur fixe, changez la contrainte de constant = à constant <= dans votre fichier d'interface. 

 enter image description here

0
Alex Brown

J'ai rencontré ce problème aussi bien avec une sous-classe UIView qui contient un UILabel comme un si ses éléments internes. Même avec l'autolayout et en essayant toutes les solutions recommandées, l'étiquette ne serait tout simplement pas plus étroite au texte. Dans mon cas, je veux seulement que l'étiquette soit une ligne.

Quoi qu’il en soit, ce qui a fonctionné pour moi a été d’ajouter une contrainte de hauteur requise pour UILabel et de la définir manuellement à la hauteur correcte lorsqu’on appelle intrinsicContentSize. Si UILabel n’est pas contenu dans un autre UIView, vous pouvez essayer de sous-classer UILabel et de fournir une implémentation similaire en définissant d’abord la contrainte de hauteur, puis en renvoyant
[super instrinsicContentSize]; au lieu de [self.containerview intrinsiceContentSize]; comme je le fais ci-dessous, qui est spécifique à ma sous-classe UIView.

- (CGSize)intrinsicContentSize
{
    CGRect expectedTextBounds = [self.titleLabel textRectForBounds:self.titleLabel.bounds limitedToNumberOfLines:1];
    self.titleLabelHeightConstraint.constant = expectedTextBounds.size.height;
    return [self.containerView intrinsicContentSize];

}

Fonctionne parfaitement maintenant sur iOS 7 et iOS 8.

0
n8tr