Dynamic 如何根据iOS 7上的内容调整UITextView的大小?
多年来我一直在使用公认的答案 在iOS 7上,无论文本内容如何,contentSize.height都将变为frame.height-8Dynamic 如何根据iOS 7上的内容调整UITextView的大小?,dynamic,height,uitextview,ios7,Dynamic,Height,Uitextview,Ios7,多年来我一直在使用公认的答案 在iOS 7上,无论文本内容如何,contentSize.height都将变为frame.height-8 在iOS 7上调整高度的工作方法是什么?这种方法似乎有效 // Code from apple developer forum - @Steve Krulewitz, @Mark Marszal, @Eric Silverberg - (CGFloat)measureHeight { if ([self respondsToSelector:@selector
在iOS 7上调整高度的工作方法是什么?这种方法似乎有效
// 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;
}
}
我赞成这种最小的代码更改:只需在
addSubview
之后和抓取框架的高度之前添加这两行代码即可
...
[scrollView1 addSubview: myTextView];
[myTextView sizeToFit]; //added
[myTextView layoutIfNeeded]; //added
CGRect frame = myTextView.frame;
...
这是测试向后兼容iOS 6注意它收缩包裹宽度。如果你只是对高度感兴趣,并且有一个固定的宽度,只需抓取新高度,但设置原始宽度,它在iOS 6和iOS 7上的工作原理与以前一样
(推测:它的大小也适合iOS 7,但布局会在稍后或在单独的线程中更新,这会立即强制布局,以便及时更新其框架,以便在同一线程中稍后几行使用其高度值。)
注意事项:
1) 您可能会也可能不会以这种方式实现外部容器的大小调整。不过,这似乎是一个常见的片段,我已经在我的项目中使用过它
2) 由于iOS 7上的sizeToFit
似乎可以按预期工作,因此您可能不需要过早添加子视图。它是否仍能在iOS 6上运行还没有经过我的测试
3) 推测:额外的layoutifneed
mid-thread可能代价高昂。在我看来,另一种选择是在布局回调上调整外部容器的大小(触发或不触发取决于操作系统是否决定是否需要布局),其中外部容器的大小调整将导致另一个布局更新。这两个更新可以与其他布局更新结合使用,以提高效率。如果您确实有这样一个解决方案,并且您可以证明它更有效,请将其添加为答案,我一定会在这里提及。因为我使用的是自动布局,所以我使用[textView sizeThatFits:CGSizeMake(textView.frame.size.width,CGFLOAT\u MAX]的值.height
要更新textView
高度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;
}
}
如果您使用的是自动布局,则可以创建一个简单的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
当然,文本视图的宽度和位置必须由程序中其他地方配置的附加约束定义
如果在IB中创建此自定义文本视图,则为文本视图提供高度约束以满足Xcode;只需确保在IB中创建的高度约束只是一个占位符(即,勾选“在构建时删除”的框)
实现UITextView
子类的另一种方法如下(此实现可能符合最佳实践):
在iOS 8中,您将从父级继承一些内容偏移量,您也需要将其删除
子类示例
// 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右侧窗格的“标尺”选项卡中约束到superview。我的问题是,我在“尾随空间到”上有一个-80分的限制
如果使用自动布局,则可以使用以下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
使用autolayout的家伙,您的sizetofit不起作用,请检查一下您的宽度限制。如果错过了宽度约束,则高度将是准确的
不需要使用任何其他API。只要一行就可以解决所有问题
[_textView sizeToFit];
在这里,我只关心高度,保持宽度固定,而忽略了故事板中文本视图的宽度限制
这是为了显示服务中的动态内容
希望这可能会有所帮助。基于其他答案,我(在Swift中)成功了。
这就解决了换行符的问题
textView.sizeToFit()
textView.layoutIfNeeded()
let height = textView.sizeThatFits(CGSizeMake(textView.frame.size.width, CGFloat.max)).height
textView.contentSize.height = height
需要自动布局。如果您使用的是iOS 7+,只需打开自动布局,将文本视图的每一侧固定到其父视图的边缘,即可正常工作。不需要额外的代码。我在UITextView
上写了一个类别:
- (CGSize)intrinsicContentSize {
return self.contentSize;
}
- (void)setContentSize:(CGSize)contentSize {
[super setContentSize:contentSize];
[self invalidateIntrinsicContentSize];
}
当UIKit
设置其contentSize
时,UITextView
调整其固有内容大小。这很好地发挥了autolayout
的作用,bilobatum给出的答案与autolayout完美结合,即对textview进行子类化
如果您想限制文本视图的高度,请添加另一个约束(我使用故事板添加了它,即高度不确定是否总是这样,但以下是正确的,因为至少iOS 10
UITextView
如果scrollEnabled==NO
,则实现intrinsicContentSize
属性。这意味着您只需确保文本视图的宽度受到足够的约束,然后就可以使用内部内容高度(通过自动布局内容拥抱/压缩阻力优先级或在手动布局期间直接使用该值)
不幸的是,这种行为没有记录在案。苹果可以很容易地为我们大家省去一些麻烦……不需要额外的高度限制、子类化等。我遇到了同样的问题。我看到他们向UIView添加了textContainer,该容器有一个大小,但我目前认为它的大小不准确。它调整了textview的大小,但我仍然没有在iOS 7中使用此选项调整textview的父元素的大小,您能分享更多详细信息吗?不知道,信息太少。但是如果您用不起作用的代码编写问题,我可以看一看。@JannieT自从我发布答案以来,我在app store上更新的每个应用程序中都使用了它,我猜大约有五个。一些文本视图是单一的行,有些是多行的。我今天可能会更新另一个,可以看看最新的OS 7.1.x。谢谢,这是我的
- (CGSize)intrinsicContentSize {
return self.contentSize;
}
- (void)setContentSize:(CGSize)contentSize {
[super setContentSize:contentSize];
[self invalidateIntrinsicContentSize];
}