如何在iOS 7上将UITextView的大小设置为其内容?
多年来,我一直在使用已接受的答案。
在iOS 7上,无论文本内容如何,contentSize.height都会变成frame.height-8。
什么是调整iOS 7的高度的工作方法?
我喜欢这个最小的代码更改:只需在addSubview
之后和抓取frame
的height
之前添加这两行
... [scrollView1 addSubview: myTextView]; [myTextView sizeToFit]; //added [myTextView layoutIfNeeded]; //added CGRect frame = myTextView.frame; ...
这是testing向后兼容iOS 6. 注意 ,它收缩包装的宽度。 如果你只是对高度感兴趣,并且有一个固定的宽度,只需抓住新的高度,但设置原来的宽度,它就像以前一样在iOS 6和7上工作。
(猜测:它的大小也适合iOS 7,但是布局稍后或在单独的线程中更新,并且这会立即强制布局,以便帧的更新及时使用其高度值几行后在同一个线程中。)
笔记:
1)你可能也可能没有这样实现外部容器的大小调整。 它似乎是一个常见的片段,而且我用它在我的项目中。
2)由于sizeToFit
似乎在iOS 7上按预期工作,你可能不需要过早的addSubView。 它是否仍然在iOS 6上工作,然后是我未经testing。
3)推测:额外layoutIfNeeded
可能是昂贵的。 我所看到的另一种方法是调整布局callback上的外部容器(根据操作系统是否需要布局来决定是否触发),外部容器的大小调整将导致另一个布局更新。 这两个更新可能会与其他布局更新相结合,以提高效率。 如果你有这样一个解决scheme,你可以certificate它更有效率,把它添加为答案,我一定会在这里提到它。
由于我使用的是自动布局,因此我使用[textView sizeThatFits:CGSizeMake(textView.frame.size.width, CGFLOAT_MAX)].height
来更新UILayoutConstraint
的高度UILayoutConstraint
。
我使用了一个适应版本的madmik的答案,消除了欺骗因素:
- (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; } }
基于其他的答案,我做了工作(在斯威夫特)。 这解决了换行符的问题。
textView.sizeToFit() textView.layoutIfNeeded() let height = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max)).height textView.contentSize.height = height
自动布局是必要的。
如果您使用的是自动布局,您可以创build一个自动调整文本视图高度以适应内容的普通UITextView
子类:
@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
当然,文本视图的宽度和位置必须由程序中其他地方configuration的附加约束来定义。
如果你在IB中创build这个自定义文本视图,给文本视图一个高度约束以满足Xcode; 只要确保在IB中创build的高度约束只是一个占位符(即,勾选“在构build时删除”的框)。
另一种实现UITextView
子类的方法如下(这个实现可能被认为是最佳实践):
@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
如果使用自动布局,则可以使用以下添加了内在高度的UITextView子类:
@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
这种方法似乎工作。
// 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; } }
如果你使用的是iOS 7+,你可以打开自动布局,将文本视图的每一边固定到其父视图的边缘,并且工作正常。 没有额外的代码需要。
在iOS 8中,您将inheritance父代的一些内容偏移量,您还需要将其从父代中删除。
一个子类的例子
// 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
在故事板中,如果使用约束,请确保在UITextView的xcode的右侧窗格的“标尺”选项卡中限制为超级视图。 我的问题是,我在“尾随空间”上有约80分的限制。
使用自动布局和你的sizetofit不工作的人,那么请检查你的宽度约束一次。 如果你错过了宽度约束,那么高度将是准确的。
不需要使用任何其他的API。 只有一条线可以解决所有问题。
[_textView sizeToFit];
在这里,我只关心高度,保持宽度不变,错过了Storyboard中TextView的宽度约束。
这是为了显示服务的dynamic内容。
希望这可能会帮助..
我用UITextView
写了一个类:
- (CGSize)intrinsicContentSize { return self.contentSize; } - (void)setContentSize:(CGSize)contentSize { [super setContentSize:contentSize]; [self invalidateIntrinsicContentSize]; }
当UIKit
设置其contentSize
, UITextView
调整其intrinsic content size
。 这与自动autolayout
很好地autolayout
。
bilobatum给出的答案完全适用于自动布局,即对textview进行子类化。
如果你想限制文本视图的高度添加另一个约束(我添加它使用故事板即高度<= 166(根据您的需要的高度))
然后在子类中将高度约束的优先级降低到750(self.heightConstraint.priority = 750)以避免在子类中添加的高度约束与在故事板上添加的高度约束之间的冲突。