27 votes

Mise en page automatique iOS: problème avec UILabels dans une vue parent de redimensionnement

J'ai une vue qui contient seulement un UILabel. Cette étiquette contient du texte multiligne. Le parent a une largeur variable qui peut être redimensionné avec un pan geste. Mon problème est que quand je fais ce redimensionnement de la UILabel ne pas recalculer sa hauteur telle que l'ensemble du contenu est encore visible, il suffit simplement de coupe.

J'ai réussi à le résoudre avec un peu de bidouille, mais c'est horriblement lent à exécuter:

- (void)layoutSubviews {

    CGSize labelSize = [self.labelDescription sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)];

    if (self.constraintHeight) {
        [self removeConstraint:self.constraintHeight];
    }

    self.constraintHeight = [NSLayoutConstraint constraintWithItem:self.labelDescription attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:labelSize.height];

    [self addConstraint:self.constraintHeight];

    [super layoutSubviews];
}

Ne devrait-elle pas automatiquement avec mise en page automatique?

MODIFIER

La structure de mon point de vue est:

UIScrollView
---> UIView
     ---> UILabel

Voici les contraintes sur le UIScrollView:

<NSLayoutConstraint:0x120c4860 H:|-(>=32)-[DescriptionView:0x85c81c0]   (Names: '|':UIScrollView:0x85db650 )>,
<NSLayoutConstraint:0x120c48a0 H:|-(32@900)-[DescriptionView:0x85c81c0] priority:900   (Names: '|':UIScrollView:0x85db650 )>,
<NSLayoutConstraint:0x120c48e0 H:[DescriptionView:0x85c81c0(<=576)]>,
<NSLayoutConstraint:0x120c4920 H:[DescriptionView:0x85c81c0]-(>=32)-|   (Names: '|':UIScrollView:0x85db650 )>,
<NSLayoutConstraint:0x120c4960 H:[DescriptionView:0x85c81c0]-(32@900)-| priority:900   (Names: '|':UIScrollView:0x85db650 )>,
<NSLayoutConstraint:0x8301450 DescriptionView:0x85c81c0.centerX == UIScrollView:0x85db650.centerX>,

Voici les contraintes sur le UIView:

<NSLayoutConstraint:0x85c4580 V:|-(0)-[UILabel:0x85bc7b0]   (Names: '|':DescriptionView:0x85c81c0 )>,
<NSLayoutConstraint:0x85c45c0 H:|-(0)-[UILabel:0x85bc7b0]   (Names: '|':DescriptionView:0x85c81c0 )>,
<NSLayoutConstraint:0x85c9f80 UILabel:0x85bc7b0.trailing == DescriptionView:0x85c81c0.trailing>,
<NSLayoutConstraint:0x85c9fc0 UILabel:0x85bc7b0.centerY == DescriptionView:0x85c81c0.centerY>

Le UILabel lui-même n'a pas de contraintes sur elle, en dehors de l'épinglage pour les bords de son parent

36voto

matt Points 60113

Bon, j'ai enfin trouvé. La solution est de mettre la preferredMaxLayoutWidth en viewDidLayoutSubviews, mais seulement après le premier tour de mise en page. Vous pouvez arranger cela tout simplement par l'envoi asynchrone de retour sur le thread principal. Donc:

- (void)viewDidLayoutSubviews {
    dispatch_async(dispatch_get_main_queue(), ^{
        self.theLabel.preferredMaxLayoutWidth = self.theLabel.bounds.size.width;
    });
}

De cette façon, vous ne définissez pas d' preferredMaxLayoutWidth jusqu' après la largeur de l'étiquette a été correctement mis en place par ses superview-contraintes qui y sont liées.

Exemple de travail du projet ici:

https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/ch23p673selfSizingLabel4/p565p579selfSizingLabel/ViewController.m

EDIT: une Autre approche! Sous-classe UILabel et remplacer layoutSubviews:

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

Le résultat est une auto-dimensionnement d'étiquette, il change automatiquement de sa hauteur pour s'adapter à son contenu, peu importe la façon dont sa largeur changements (en supposant que sa largeur est modifié par contraintes / mise en page).

26voto

simon Points 781

J'ai résolu ce problème après avoir signalé un bogue avec Apple. Le problème que le texte multiligne nécessite une approche en deux passes de la mise en page et tout repose sur la propriété preferredMaxLayoutWidth

Voici le code pertinent qui doit être ajouté à un contrôleur de vue qui contient une étiquette multiligne:

 - (void)viewWillLayoutSubviews
{
    // Clear the preferred max layout width in case the text of the label is a single line taking less width than what would be taken from the constraints of the left and right edges to the label's superview
    [self.label setPreferredMaxLayoutWidth:0.];
}

- (void)viewDidLayoutSubviews
{
    // Now that you know what the constraints gave you for the label's width, use that for the preferredMaxLayoutWidth-so you get the correct height for the layout
    [self.label setPreferredMaxLayoutWidth:[self.label bounds].size.width];

    // And then layout again with the label's correct height.
    [self.view layoutSubviews];
}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X