Comment puis-je dimensionner UITextView par rapport à son contenu sur iOS 7?

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

sur iOS 7, le contensize.hauteur devient le cadre.hauteur-8, quel que soit le contenu du texte.

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

64
demandé sur Community 2013-09-27 15:24:37

13 réponses

je suis favorable à ce changement de code minimal: il suffit d'ajouter ces deux lignes après addSubview et avant de saisir le height du frame

...
[scrollView1 addSubview: myTextView];

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

CGRect frame = myTextView.frame;
...

ceci est testé à l'envers compatible avec iOS 6. NOTE qu'elle rétrécit la largeur. Si vous êtes juste intéressé par la hauteur et avoir une largeur fixe, il suffit de saisir la nouvelle hauteur, mais définir la largeur d'origine, et il fonctionne tout comme avant sur les deux iOS 6 et 7.

(spéculation: il fait la taille pour s'adapter sur iOS 7 aussi bien, mais la mise en page est mise à jour plus tard ou dans un thread séparé, et que cela force la mise en page immédiatement de sorte que son cadre est mis à jour à temps pour l'utilisation de sa valeur de hauteur quelques lignes plus tard dans le même thread.)

NOTES:

1) Vous avez peut-être ou non implémenté le conteneur extérieur redimensionné de cette façon. Il ne semble être un snippet commune, bien que, et je l'ai utilisé dans mes projets.

2) puisque sizeToFit semble fonctionner comme prévu sur iOS 7, vous n'avez probablement pas besoin de l'addSubView prématurée. Je n'ai pas vérifié s'il fonctionnerait encore sur iOS 6.

3) spéculation: le layoutIfNeeded supplémentaire mid-thread pourrait être coûteux. L'alternative que je vois c'est de redimensionner le conteneur externe sur le rappel de layout (lancé ou non selon si L'OS décide si la mise en page est nécessaire ou non) où le conteneur externe redimensionner va provoquer une autre mise à jour. Les deux mises à jour peuvent être combinées avec d'autres mises à jour pour être plus efficaces. Si vous do ont une telle solution et vous pouvez montrer qu'il est plus efficace, l'ajouter comme réponse et je ne manquerai pas de le mentionner ici.

55
répondu Henrik Erlandsson 2013-10-21 06:43:49

puisque j'utilise la mise en page automatique, j'utilise la valeur de [textView sizeThatFits:CGSizeMake(textView.frame.size.width, CGFLOAT_MAX)].height pour mettre à jour la constant de la textView 's height UILayoutConstraint .

33
répondu ma11hew28 2013-10-17 20:17:50

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;
    }
}
17
répondu tarmes 2013-10-21 11:26:11

basé sur d'autres réponses, j'ai fait en sorte que cela fonctionne(dans Swift). Cela résout le problème avec le caractère newline.

textView.sizeToFit()
textView.layoutIfNeeded()
let height = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max)).height
textView.contentSize.height = height

mise en page automatique est nécessaire.

16
répondu MKatleast3 2015-01-11 06:48:05

si vous utilisez la mise en page automatique, vous pouvez créer une sous-classe UITextView triviale qui prend en taille la hauteur de la vue texte pour s'adapter au 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, la largeur et la position de la vue texte doivent être définies par des contraintes supplémentaires configurées ailleurs dans le programme.

si vous créez cette vue texte personnalisée dans IB, donnez à la vue texte une contrainte de hauteur pour satisfaire Xcode; assurez-vous simplement que la contrainte de hauteur créée l'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 la sous-classe UITextView est la suivante (Cette mise en œuvre pourrait être considérée comme une pratique exemplaire):

@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
14
répondu bilobatum 2014-02-24 01:06:37

si vous utilisez auto-layout, vous pouvez utiliser la sous-classe UITextView suivante qui ajoute une hauteur intrinsèque:

@implementation SelfSizingTextView

- (void)setText:(NSString *)text
{
    [super setText:text];
    [self invalidateIntrinsicContentSize];
}

- (void)setFont:(UIFont *)font
{
    [super setFont:font];
    [self invalidateIntrinsicContentSize];
}

- (CGSize)intrinsicContentSize
{
    CGFloat width = self.frame.size.width;
    CGSize size = [self sizeThatFits:CGSizeMake(width, MAXFLOAT)];
    return CGSizeMake(UIViewNoIntrinsicMetric, size.height);
}

@end
4
répondu phatmann 2014-12-05 22:08:05

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;
}
}
3
répondu madmik3 2013-09-29 22:58:36

si vous utilisez iOS 7+, vous pouvez simplement activer la mise en page automatique, épingler chacun des côtés de la vue texte au bord de sa vue mère, et cela fonctionne très bien. Pas besoin de code supplémentaire.

1
répondu Swindler 2015-03-08 00:05:37

dans iOS 8 Vous hériterez d'un certain contenu offset du parent, dont vous devez vous débarrasser aussi.

exemple de sous-classe

// Originally from https://github.com/Nikita2k/resizableTextView

#import "ResizableTextView.h"

@implementation ResizableTextView

- (void) updateConstraints {

    // calculate contentSize manually
    // ios7 doesn't calculate it before viewDidAppear and we'll get here before
    CGSize contentSize = [self sizeThatFits:CGSizeMake(self.frame.size.width, FLT_MAX)];

    // set the height constraint to change textView height
    [self.constraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *constraint, NSUInteger idx, BOOL *stop) {
        if (constraint.firstAttribute == NSLayoutAttributeHeight) {
            constraint.constant = contentSize.height;
            *stop = YES;
        }
    }];

    [super updateConstraints];
}

- (void)setContentOffset:(CGPoint)contentOffset
{
    // In iOS 8 we seem to be inheriting the content offset from the parent.
    // I'm not interested
}

@end
0
répondu Johan Forssell 2014-09-16 11:57:27

dans storyboard, si vous utilisez des contraintes, assurez-vous que vous êtes contraint à votre superview dans l'onglet 'ruler' du volet de droite de xcode pour L'UITextView. Mon problème était que j'avais une contrainte de -80 pts sur la "Fuite de l'espace".

enter image description here

0
répondu DanWebster 2014-11-25 16:56:53

les gars utilisant autolayout et votre sizetofit ne fonctionne pas, alors s'il vous plaît vérifier votre contrainte de largeur une fois. Si vous avez manqué la contrainte de largeur, alors la hauteur sera précise.

pas besoin d'utiliser une autre API. juste une ligne qui permettrait de résoudre tous les problème.

[_textView sizeToFit];

ici, Je ne me souciais que de la hauteur, en gardant la largeur fixe et en ayant manqué la contrainte de largeur de mon TextView en storyboard.

et c'était montrer le contenu dynamique des services.

J'espère que cela pourrait aider..

0
répondu Swaroop S 2015-01-02 07:02:52

j'ai écrit une catégorie sur UITextView :

- (CGSize)intrinsicContentSize {
    return self.contentSize;
}

- (void)setContentSize:(CGSize)contentSize {
    [super setContentSize:contentSize];
    [self invalidateIntrinsicContentSize];
}

lorsque UIKit règle son contentSize , UITextView règle son intrinsic content size . Ça joue bien avec autolayout .

0
répondu Mert Buran 2016-03-30 16:14:01

la réponse donnée par bilobatum a fonctionné parfaitement avec auto layout, I. e subclassing the textview.

si vous voulez limiter la hauteur de la vue texte ajouter une autre contrainte (je l'ai ajouté en utilisant storyboard i.e. hauteur < = 166 (hauteur selon votre besoin))

ensuite, à l'intérieur de la sous-classe, réduire la priorité de la contrainte de hauteur à 750 (self.heightConstraint.priorité = 750) pour éviter tout conflit entre la contrainte de hauteur ajoutée dans la sous-classe et la contrainte de hauteur contrainte ajoutée sur le storyboard.

0
répondu Sunil Bhosale 2017-03-06 08:06:52