65 votes

Comment dimensionner un UITextView en fonction de son contenu sur iOS 7?

J'utilise la réponse acceptée ici depuis des années.

Sur iOS 7, contentSize.height devient frame.height-8, quel que soit le contenu du texte.

Quelle est la méthode de travail pour ajuster la hauteur sur iOS 7?

55voto

Henrik Erlandsson Points 1580

Je préfère ce code minimal changer: il suffit d'ajouter ces deux lignes après addSubview et, avant de prendre l' height de la frame

...
[scrollView1 addSubview: myTextView];

    [myTextView sizeToFit]; //added
    [myTextView layoutIfNeeded]; //added

CGRect frame = myTextView.frame;
...

Ceci est testé compatible avec iOS 6. NOTEZ qu'il shrink-roulés de la largeur. Si vous êtes simplement intéressé à la hauteur et ont une largeur fixe, il suffit de saisir la nouvelle hauteur, mais de définir la largeur d'origine, et il fonctionne comme avant sur les deux iOS 6 et 7.

(Spéculation: il n'taille pour s'adapter à iOS 7, mais la mise en page est mise à jour plus tard ou dans un thread séparé, et que cela oblige la disposition immédiatement, de sorte que son image est mise à jour dans le temps pour l'utilisation de sa valeur de hauteur de quelques lignes plus loin dans le même thread.)

NOTES:

1) Vous pourrait ou ne pourrait pas avoir mis en œuvre le contenant extérieur redimensionner cette façon. Il ne semble pas être un bon extrait, bien que, et je l'ai utilisé dans mes projets.

2) Depuis sizeToFit semble fonctionner comme prévu sur iOS 7, il est probable que vous n'avez pas besoin de le prématuré addSubView. S'il faudra encore travailler sur iOS 6 n'est pas testé par moi.

3) la Spéculation: Les extra - layoutIfNeeded à la mi-thread peut être coûteux. L'alternative que je vois c'est pour redimensionner l'extérieur du conteneur sur la mise en page de rappel (licenciés ou non, selon si le système d'exploitation décide si la mise en page est nécessaire ou non) lorsque le récipient externe redimensionner va provoquer une autre mise à jour. Les deux mises à jour qui pourraient être combinés avec d'autres mise en page des mises à jour pour être plus efficace. Si vous ne avoir une telle solution et on peut montrer qu'elle est plus efficace, l'ajouter en tant que réponse et je vais être sûr de le mentionner ici.

33voto

MattDiPasquale Points 23842

Depuis que je suis en utilisant la mise en page automatique, j'utilise la valeur de [textView sizeThatFits:CGSizeMake(textView.frame.size.width, CGFLOAT_MAX)].height mettre à jour le constant du textView hauteur de UILayoutConstraint .

17voto

tarmes Points 6363

J'utilise une version adaptée de la réponse de madmik qui élimine le facteur fudge:

 - (CGFloat)measureHeightOfUITextView:(UITextView *)textView
{
    if ([textView respondsToSelector:@selector(snapshotViewAfterScreenUpdates:)])
    {
        // This is the code for iOS 7. contentSize no longer returns the correct value, so
        // we have to calculate it.
        //
        // This is partly borrowed from HPGrowingTextView, but I've replaced the
        // magic fudge factors with the calculated values (having worked out where
        // they came from)

        CGRect frame = textView.bounds;

        // Take account of the padding added around the text.

        UIEdgeInsets textContainerInsets = textView.textContainerInset;
        UIEdgeInsets contentInsets = textView.contentInset;

        CGFloat leftRightPadding = textContainerInsets.left + textContainerInsets.right + textView.textContainer.lineFragmentPadding * 2 + contentInsets.left + contentInsets.right;
        CGFloat topBottomPadding = textContainerInsets.top + textContainerInsets.bottom + contentInsets.top + contentInsets.bottom;

        frame.size.width -= leftRightPadding;
        frame.size.height -= topBottomPadding;

        NSString *textToMeasure = textView.text;
        if ([textToMeasure hasSuffix:@"\n"])
        {
            textToMeasure = [NSString stringWithFormat:@"%@-", textView.text];
        }

        // NSString class method: boundingRectWithSize:options:attributes:context is
        // available only on ios7.0 sdk.

        NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
        [paragraphStyle setLineBreakMode:NSLineBreakByWordWrapping];

        NSDictionary *attributes = @{ NSFontAttributeName: textView.font, NSParagraphStyleAttributeName : paragraphStyle };

        CGRect size = [textToMeasure boundingRectWithSize:CGSizeMake(CGRectGetWidth(frame), MAXFLOAT)
                                                  options:NSStringDrawingUsesLineFragmentOrigin
                                               attributes:attributes
                                                  context:nil];

        CGFloat measuredHeight = ceilf(CGRectGetHeight(size) + topBottomPadding);
        return measuredHeight;
    }
    else
    {
        return textView.contentSize.height;
    }
}
 

15voto

bilobatum Points 3961

Si vous utilisez la Mise en page Automatique, vous pouvez créer un trivial UITextView sous-classe que l'auto-tailles de l'affichage de texte en hauteur pour s'adapter le contenu:

@interface ContentHeightTextView : UITextView

@end

@interface ContentHeightTextView ()
@property (nonatomic, strong) NSLayoutConstraint *heightConstraint;
@end

@implementation ContentHeightTextView

- (void)layoutSubviews
{
    [super layoutSubviews];

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

    if (!self.heightConstraint) {
        self.heightConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:1.0f constant:size.height];
        [self addConstraint:self.heightConstraint];
    }

    self.heightConstraint.constant = size.height;

    [super layoutSubviews];
}

@end

Bien sûr, l'affichage de texte sur la largeur et la position doit être défini par des contraintes supplémentaires configuré ailleurs dans le programme.

Si vous créez ce texte personnalisé en vue de l'IB, donner le texte à afficher une hauteur de contrainte afin de satisfaire Xcode, il suffit de s'assurer que la hauteur de la contrainte créée en IB est simplement un espace réservé (c'est à dire, cochez la case "Supprimer au moment de la construction").

enter image description here

Une autre façon de mettre en œuvre l' UITextView sous-classe est comme suit (cette mise en œuvre pourrait être admissible en tant que meilleure pratique):

@interface ContentHeightTextView ()
@property (nonatomic, strong) NSLayoutConstraint *heightConstraint;
@end

@implementation ContentHeightTextView

- (void)layoutSubviews
{
    [super layoutSubviews];

    [self setNeedsUpdateConstraints];
}

- (void)updateConstraints
{
    CGSize size = [self sizeThatFits:CGSizeMake(self.bounds.size.width, FLT_MAX)];

    if (!self.heightConstraint) {
        self.heightConstraint = [NSLayoutConstraint constraintWithItem:self attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:0 multiplier:1.0f constant:size.height];
        [self addConstraint:self.heightConstraint];
    }

    self.heightConstraint.constant = size.height;
    [super updateConstraints];
}

@end

3voto

madmik3 Points 4251

cette méthode semble fonctionner.

 // Code from apple developer forum - @Steve Krulewitz, @Mark Marszal, @Eric Silverberg
- (CGFloat)measureHeight
{
if ([self respondsToSelector:@selector(snapshotViewAfterScreenUpdates:)])
{
    CGRect frame = internalTextView.bounds;
    CGSize fudgeFactor;
    // The padding added around the text on iOS6 and iOS7 is different.
    fudgeFactor = CGSizeMake(10.0, 16.0);

    frame.size.height -= fudgeFactor.height;
    frame.size.width -= fudgeFactor.width;

    NSMutableAttributedString* textToMeasure;
    if(internalTextView.attributedText && internalTextView.attributedText.length > 0){
        textToMeasure = [[NSMutableAttributedString alloc] initWithAttributedString:internalTextView.attributedText];
    }
    else{
        textToMeasure = [[NSMutableAttributedString alloc] initWithString:internalTextView.text];
        [textToMeasure addAttribute:NSFontAttributeName value:internalTextView.font range:NSMakeRange(0, textToMeasure.length)];
    }

    if ([textToMeasure.string hasSuffix:@"\n"])
    {
        [textToMeasure appendAttributedString:[[NSAttributedString alloc] initWithString:@"-" attributes:@{NSFontAttributeName: internalTextView.font}]];
    }

    // NSAttributedString class method: boundingRectWithSize:options:context is
    // available only on ios7.0 sdk.
    CGRect size = [textToMeasure boundingRectWithSize:CGSizeMake(CGRectGetWidth(frame), MAXFLOAT)
                                              options:NSStringDrawingUsesLineFragmentOrigin
                                              context:nil];

    return CGRectGetHeight(size) + fudgeFactor.height;
}
else
{
    return self.internalTextView.contentSize.height;
}
}
 

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