iOS7 UITextView 的 contentsize.height 替代方案

78

我正在将一个iOS 6.1的应用程序移植到iOS 7。我的布局中有一个UITextView,它具有固定的宽度,但其高度基于其内容大小。在iOS 6.1中,检查contentsize.height并将其设置为textview的框架高度就足够了,但在iOS 7上不起作用。

那么,我该如何创建一个UITextView,它具有固定的宽度,但其高度根据所显示的文本而动态变化?

注意:我是通过代码创建这些视图,而不是使用Interface Builder。

9个回答

179

使用以下代码,您可以根据固定的宽度更改UITextView的高度(在iOS 7及以前的版本中有效):

- (CGFloat)textViewHeightForAttributedText:(NSAttributedString *)text andWidth:(CGFloat)width
{
    UITextView *textView = [[UITextView alloc] init];
    [textView setAttributedText:text];
    CGSize size = [textView sizeThatFits:CGSizeMake(width, FLT_MAX)];
    return size.height;
}

使用此函数,您可以将NSAttributedString和固定宽度作为输入,返回所需的高度。

如果您想从特定字体的文本计算框架,则需要使用以下代码:

- (CGSize)text:(NSString *)text sizeWithFont:(UIFont *)font constrainedToSize:(CGSize)size
{
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0"))
    {
        CGRect frame = [text boundingRectWithSize:size
                                          options:(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading)
                                       attributes:@{NSFontAttributeName:font}
                                          context:nil];
        return frame.size;
    }
    else
    {
        return [text sizeWithFont:font constrainedToSize:size];
    }
}

您可以在项目的prefix.pch文件中添加SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO,例如:

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)

你也可以用以下内容替换之前的测试SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)

if ([text respondsToSelector:@selector(boundingRectWithSize:options:attributes:context:)])‌

我只能在24小时后把它给你。耐心等待,它就是你的 :) - Zoltan Varadi
16
也许你应该将答案链接到它们所在的位置?https://dev59.com/W2Ml5IYBdhLWcg3we283 - andrew-caulfield
@Jordan Montel,对于另一个问题,如果我有固定的宽度和高度,如何找到分配字符串的最佳字体大小?我在iOS7上也遇到了类似的问题(在iOS6上,我可以解决)。 - LiangWang
1
@Jacky:试错法;通过增加或减少大小1,直到计算出的高度太大。或者将其大小设置为1000并自动调整大小以适应。 - Cœur
这个答案已经是错误的了。尝试在文本字段中输入大量文本,突然出现了错误的大小?至少NSStringDrawingUsesFontLeading是额外的。 - user2159978
(NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading) 这对我很有帮助。 - samthui7

42

这对我在iOS6和7上有效:

CGSize textViewSize = [self.myTextView sizeThatFits:CGSizeMake(self.myTextView.frame.size.width, FLT_MAX)];
    self.myTextView.height = textViewSize.height;

嗨,是的,对我来说这个代码可以工作... 我使用了 "txtviewHeight.frame.size.height" 而不是 "txtviewHeight.contentSize.height". - Mani
为什么我在类型为“UITextView”的对象上找不到属性高度(height)? - Hexark
@Hexark textView.bounds.size.height @Hexark textView.bounds.size.height - user

21
在iOS7中,UITextView使用NSLayoutManager来布局文本:
// If YES, then the layout manager may perform glyph generation and layout for a given portion of the text, without having glyphs or layout for preceding portions.  The default is NO.  Turning this setting on will significantly alter which portions of the text will have glyph generation or layout performed when a given generation-causing method is invoked.  It also gives significant performance benefits, especially for large documents.
@property(NS_NONATOMIC_IOSONLY) BOOL allowsNonContiguousLayout;

禁用allowsNonContiguousLayout来修复contentSize

textView.layoutManager.allowsNonContiguousLayout = NO;

7
你的回答虽然不符合问题,但解决了我的问题,所以我点赞 :) - vietstone
这对我来说并没有解决内容大小的问题。滚动被禁用了。 - Dvole
这段代码解决了我的问题,但是你评论中的“generation-causing method”是什么意思? - Code Farmer

15

使用这个小函数

-(CGSize) getContentSize:(UITextView*) myTextView{
    return [myTextView sizeThatFits:CGSizeMake(myTextView.frame.size.width, FLT_MAX)];
}

对我来说,在iOS6和iOS7上都可以工作。谢谢! - Paul Brady

4

我的最终解决方案是基于HotJard的,但包括文本容器顶部和底部插图,而不是使用2 * fabs(textView.contentInset.top):

- (CGFloat)textViewHeight:(UITextView *)textView
{
    return ceilf([textView.layoutManager usedRectForTextContainer:textView.textContainer].size.height +
                 textView.textContainerInset.top +
                 textView.textContainerInset.bottom);
}

3
有更简单的解决方案,可以使用这种方法:
+(void)adjustTextViewHeightToContent:(UITextView *)textView;
{
    if([[UIDevice currentDevice].systemVersion floatValue] >= 7.0f){
        textView.height = [textView.layoutManager usedRectForTextContainer:textView.textContainer].size.height+2*fabs(textView.contentInset.top);
    }else{
        textView.height = textView.contentSize.height;
    }
}
更新:仅用于显示文本(isEditable = NO)

看起来你的解决方案不起作用。当TextView到达第二行时,usedRectForTextContainer方法返回相同的高度。 - Valerii Pavlov
哦,也许我只是用它来显示文本。 - HotJard

1
   _textView.textContainerInset = UIEdgeInsetsZero;
   _textView.textContainer.lineFragmentPadding = 0;

只是不要忘记 lineFragmentPadding


0

@Ana和@Blake Hamilton的解决方案使用Swift

var contentHeight: CGFloat = textView.sizeThatFits(textView.frame.size).height

对我来说好的一点是,当 isScrollEnable 被设置为 false 时,它也会返回正确的 contentSize。如果设置为false,它将返回文本视图的框架大小而非内容大小。

0
简单的解决方案 - textView.isScrollEnabled = false,当在另一个滚动视图或带有UITableViewAutomaticDimension的tableView单元格中时,效果完美。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接