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?
Je suis favorable à ce changement de code minimal: ajoutez simplement ces deux lignes après addSubview
et avant de saisir la height
de la frame
...
[scrollView1 addSubview: myTextView];
[myTextView sizeToFit]; //added
[myTextView layoutIfNeeded]; //added
CGRect frame = myTextView.frame;
...
Ceci est testé rétro-compatible avec iOS 6.NOTEqu'il rétrécit envelopper la largeur. Si vous êtes simplement intéressé par la hauteur et que vous avez une largeur fixe, saisissez simplement la nouvelle hauteur mais définissez la largeur d'origine, et cela fonctionne comme avant sous iOS 6 et 7.
(Speculation: la taille s'adapte également à iOS 7, mais la mise en page est mise à jour ultérieurement ou dans un thread séparé, ce qui force la mise en page immédiatement afin que son cadre soit mis à jour à temps pour pouvoir utiliser sa hauteur lignes plus tard dans le même fil.)
REMARQUES:
1) Vous avez peut-être ou non implémenté le redimensionnement du conteneur externe de cette manière. Cela semble être un extrait courant, cependant, 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 la méthode prématurée addSubView. Que cela fonctionne toujours sur iOS 6 n'est pas testé par moi.
3) Spéculation: le coût moyen supplémentaire de layoutIfNeeded
peut être coûteux. L'alternative telle que je la vois est de redimensionner le conteneur externe sur le rappel de présentation (déclenché ou non, selon que le système d'exploitation décide si la présentation est nécessaire ou non), le redimensionnement du conteneur externe provoquant une autre mise à jour de la présentation. Les deux mises à jour peuvent être combinées avec d'autres mises à jour de présentation pour être plus efficaces. Si vous avez une telle solution et que vous pouvez montrer qu'elle est plus efficace, ajoutez-la comme réponse et je ne manquerai pas de le mentionner ici.
Depuis que 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
.
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;
}
}
Sur la base d’autres réponses, j’ai réussi à le faire fonctionner (dans Swift) . Ceci résout le problème du caractère de nouvelle ligne.
textView.sizeToFit()
textView.layoutIfNeeded()
let height = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max)).height
textView.contentSize.height = height
La mise en page automatique est nécessaire.
Si vous utilisez la mise en page automatique, vous pouvez créer une sous-classe UITextView
triviale qui redimensionne automatiquement la hauteur de la vue texte pour l'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 entendu, 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 de texte personnalisée dans IB, attribuez à cette dernière une contrainte de hauteur afin de satisfaire à Xcode. assurez-vous simplement que la contrainte de hauteur créée dans IB est simplement un espace réservé (c.-à-d., cochez la case "Supprimer au moment de la construction").
Une autre manière de mettre en œuvre la sous-classe UITextView
est la suivante (cette mise en œuvre peut être qualifiée de 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
Si vous utilisez la disposition automatique, 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
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;
}
}
Si vous utilisez iOS 7+, vous pouvez simplement activer la mise en page automatique, épingler chacun des côtés de la vue texte sur le bord de sa vue parent, et tout fonctionne correctement. Aucun code supplémentaire requis.
Dans le storyboard, si vous utilisez des contraintes, assurez-vous que votre superview est limité dans l'onglet "règle" du volet de droite du xcode pour UITextView. Mon problème était que j'avais une contrainte de -80 pts sur le 'Trailing space to'.
La réponse donnée par bilobatum a parfaitement fonctionné. Avec la mise en page automatique, c’est-à-dire la sous-classification textview.
Si vous souhaitez limiter la hauteur de la vue texte, ajoutez une autre contrainte (je l'ai ajoutée à l'aide d'un storyboard, c'est-à-dire hauteur <= 166 (hauteur selon vos besoins)).
Ensuite, dans la sous-classe, réduisez la priorité de contrainte de hauteur à 750 (self.heightConstraint.priority = 750) pour éviter les conflits entre la contrainte de hauteur ajoutée dans la sous-classe et la contrainte de hauteur ajoutée dans le storyboard.
Dans iOS 8, vous héritez d'un contenu décalé du parent, dont vous devez également vous débarrasser.
Un 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
J'ai écrit une catégorie sur UITextView
:
- (CGSize)intrinsicContentSize {
return self.contentSize;
}
- (void)setContentSize:(CGSize)contentSize {
[super setContentSize:contentSize];
[self invalidateIntrinsicContentSize];
}
Lorsque UIKit
définit sa contentSize
, UITextView
ajuste son intrinsic content size
. Cela joue bien avec autolayout
.
Les gars utilisant autolayout et votre taille ne fonctionnant pas, veuillez vérifier votre contrainte de largeur une fois. Si vous avez manqué la contrainte de largeur, la hauteur sera précise.
Pas besoin d'utiliser une autre API. une seule ligne résoudrait tout le problème.
[_textView sizeToFit];
Ici, je ne m'occupais que de la hauteur, en maintenant la largeur fixe et j'avais manqué la contrainte de largeur de TextView dans le storyboard.
Et c'était pour montrer le contenu dynamique des services.
J'espère que cela pourrait aider ..