iOS Autolayout: problème avec UILabels dans une vue parent redimensionnement

j'ai une vue qui ne contient qu'un UILabel. Cette étiquette contient du texte multiligne. Le parent a une largeur variable qui peut être redimensionnée avec un geste de pan. Mon problème est que lorsque je fais ce redimensionnement L'UILabel ne recalcule pas sa hauteur de sorte que tout le contenu est encore visible, il Coupe simplement.

j'ai réussi à le réparer avec un peu d'un hack mais il est horriblement lent à courir:

- (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];
}

ça ne devrait pas arriver automatiquement avec mise en page automatique?

MODIFIER

la structure de mon point de vue est:

UIScrollView
---> UIView
     ---> UILabel

Voici les contraintes sur L'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 L'uivi:

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

L'UILabel lui-même n'a aucune contrainte sur elle, à part de l'épingler aux bords de son parent

30
demandé sur simon 2012-10-31 04:50:32

3 réponses

OK, j'ai finalement réussi. La solution est de placer le preferredMaxLayoutWidth dans viewDidLayoutSubviews , mais seulement après le premier tour de la disposition. Vous pouvez organiser cela simplement en envoyant asynchrone de nouveau sur le fil principal. So:

- (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 preferredMaxLayoutWidth avant après la largeur de l'étiquette a été correctement définie par ses contraintes relatives à superview.

exemple pratique projet ici:

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

EDIT: une Autre approche! Sous-classe UILabel et surrégime layoutSubviews :

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

le résultat est une étiquette d'auto-dimensionnement - il change automatiquement sa hauteur pour s'adapter à son contenu quelle que soit sa largeur modifications (en supposant que sa largeur est modifiée par les contraintes / layout).

41
répondu matt 2013-01-25 02:38:39

j'ai corrigé ce problème après avoir soulevé un bug avec Apple. Le problème que le texte multiligne nécessite une approche de deux-pass à 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];
}
26
répondu simon 2012-11-19 04:09:43

dans Xcode 6.1 Pour iOS 7/8, j'ai pu faire fonctionner cela en positionnant simplement preferredMaxLayoutWidth dans une méthode de setter qui est appelée sur ma vue pour afficher le texte de l'étiquette. Je suppose que c'était 0 pour commencer. Exemple ci-dessous, où self.teachPieceLabel est l'étiquette. L'étiquette est câblée avec des contraintes aux côtés d'autres étiquettes dans une vue dans Interface Builder.

- (void)setTeachPieceText:(NSString *)teachPieceText {
      self.teachPieceLabel.text = teachPieceText;
      [self.teachPieceLabel setPreferredMaxLayoutWidth:[self.teachPieceLabel bounds].size.width];
}
4
répondu Evan R 2014-11-24 21:34:00