sizeWithFont:constrainedToSize:lineBreakMode:和textView.contentSize.height之间的差异

4

我正在使用方法[string sizeWithFont:constrainedToSize:lineBreakMode:]来估计我正在调整大小的textView的高度。然而,它似乎总是返回错误的大小。为了调试,我编写了以下代码:

self.textView.font = [UIFont systemFontOfSize:14.0]; // It was this before, anyways
NSLog(@"Real width: %lf %lf", self.textView.contentSize.width, self.textView.frame.size.width);
NSLog(@"Real height: %lf", self.textView.contentSize.height);
NSLog(@"Estimated width: %lf", kOTMessageCellTextWidth);
NSLog(@"Estimated height: %lf", ([message.message sizeWithFont:[UIFont systemFontOfSize:14.0]
                                             constrainedToSize:CGSizeMake(kOTMessageCellTextWidth, CGFLOAT_MAX)
                                                 lineBreakMode:UILineBreakModeWordWrap].height));

然而,上述代码揭示了我得到的不一致的结果:
实际宽度:223.000000 223.000000 实际高度:52.000000 预计宽度:223.000000 预计高度:36.000000 实际宽度:223.000000 223.000000 实际高度:142.000000 预计宽度:223.000000 预计高度:126.000000 实际宽度:223.000000 223.000000 实际高度:142.000000 预计宽度:223.000000 预计高度:126.000000
我在this similar question中注意到(显然)textView有一些填充限制其实际宽度。那里的建议是将传递给sizeWithFont:的宽度减小一些数字。推荐数字是11。
我的问题是:有没有一种通过编程实际检索此值的方法,或者有我错过的说明这个数字的文档?看起来这个数字应该是可用的,而且不需要猜测和检查,但是我找不到一个可靠的方法来识别它。
提前感谢。

如果对大家有所帮助,我的“魔法数字”是从我限制的宽度中减去16.0f(请注意,尽管是浮点数,但该数字会四舍五入到最近的像素;不要试图使用小数),并在通过sizeWithFont:返回的高度中加上16 - Ashoat
6个回答

6

好的,在进行了一些(重新)搜索后,我得出了以下结论。

UITextView每侧有8像素的内边距。但是,与UILabel不同(以及sizeWithFont方法的结果),行高也不同。

在我的情况下,字体是Arial,大小为16pt,文本视图的行高比uilabel的行高高4.5个点。所以:

  • 我使用减去16像素宽度的sizeWithFont方法来获得结果(cSize
  • 我通过cSize.height / font.lineHeight计算出lineCount
  • 并将cSize.height + (lineCount * 4.5) + 16.0用作文本视图的最终高度

代码(不是精确的代码):

CGSize constraintSize = CGSizeMake(250.0 - 16.0, MAXFLOAT);
CGSize labelSize = [message.text sizeWithFont:[UIFont fontWithName:@"Arial" size:16.0] constrainedToSize:constraintSize lineBreakMode:UILineBreakModeWordWrap];
CGFloat lineCount = labelSize.height / [UIFont fontWithName:@"Arial" size:16.0].lineHeight;
CGFloat additional = lineCount * 4.5;       // for 16.0 pt Arial for now
return labelSize.height + additional + 16.0;    // TextView fix

我仍然遇到一些问题,而且不太喜欢我解决问题的方式。但对于这个问题来说似乎还可以。如果有人提出一个合理的解决方案,那就太好了。


更新:行高的差异仅适用于日语文本。这会引起更多麻烦。 - Furkan Mustafa

4

修复很简单,因为UITextView每侧有8px的内边距,所以在计算文本的实际尺寸时,应先减去这16px内边距(每侧8px),然后得出正确结果。请参考以下代码片段:

// self.descView is an UITextView
// UITEXTVIEW_TEXT_PADDING is 8.0
CGSize constrainedSize = CGSizeMake(self.descView.contentSize.width-UITEXTVIEW_TEXT_PADDING*2, MAXFLOAT);
CGSize trueSize = [self.descView.text sizeWithFont:self.descView.font 
                                 constrainedToSize:constrainedSize 
                                     lineBreakMode:UILineBreakModeWordWrap];

CGSize contentSize = self.descView.contentSize;
contentSize.height = trueSize.height;
self.descView.contentSize = contentSize;

frame = self.descView.frame;
frame.size.height = contentSize.height ;
self.descView.frame = frame;

结果应该是正确的。

3

NSString sizeWithFont 是使用 Core Text 计算的。而 UITextView 使用 Webview(内部)来呈现其内容。

这种差异会导致各种问题。例如,在不同位置选择断行 - 您会注意到逗号分隔的列表和某些数学表达式字符串将在 Core Text 和 UITextView 之间的不同符号处断开。这经常导致测量空格以适合您的字符串时出现一行错误。

如果您想要国际字符支持,那么您可以忘记 Core Text 行高与 UITextView 匹配(无论您尝试多少种段落样式和行高组合!)。

我发现可靠地估计给定 NSString 的 UITextView 大小的唯一方法是...实际构造 UITextView 并读取其适合的大小(减去 8px 填充)。

这里有一个 UIKitLineMetrics 类,可以做到这一点。

它保留了两个“虚拟”文本视图。您可以使用所需的字体和布局宽度更新实例,并从中读取单行和多行大小。

UIKitLineMetrics.h

#import <UIKit/UIKit.h>

@interface UIKitLineMetrics : NSObject

@property (nonatomic, strong) NSMutableAttributedString *attributedString;

- (CGSize) UIKitLineSizeForText:(NSString*)text;
- (CGSize) UIKitSingleLineSizeForText:(NSString*)text;

- (void) updateWithFont:(UIFont*)font andWidth:(CGFloat)width;

@end

UIKitLineMetrics.m

#import "UIKitLineMetrics.h"

@interface UIKitLineMetrics ()

@property (nonatomic, strong) UITextView *measuringTextView;
@property (nonatomic, strong) UITextView *measuringSingleTextView;

@end

@implementation UIKitLineMetrics

@synthesize measuringTextView;
@synthesize measuringSingleTextView;
@synthesize attributedString;

- (id) init
{
    self = [super init];

    if( self )
    {
        attributedString = [[NSMutableAttributedString alloc] init];
        measuringTextView = [[UITextView alloc] initWithFrame:CGRectZero];
        measuringSingleTextView = [[UITextView alloc] initWithFrame:CGRectZero];
    }

    return self;
}

- (CGSize) UIKitLineSizeForText:(NSString*)text
{
    measuringTextView.text = [text stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];

    CGSize sz = [measuringTextView sizeThatFits:CGSizeMake(measuringTextView.frame.size.width, CGFLOAT_MAX)];

    return CGSizeMake(sz.width - 16, sz.height - 16);
}

- (CGSize) UIKitSingleLineSizeForText:(NSString*)text
{
    measuringSingleTextView.text = [text stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];

    CGSize sz = [measuringSingleTextView sizeThatFits:CGSizeMake(measuringSingleTextView.frame.size.width, CGFLOAT_MAX)];

    return CGSizeMake(sz.width - 16, sz.height - 16);
}

- (void) updateWithFont:(UIFont*)font andWidth:(CGFloat)width
{
    measuringTextView.font = font;    
    measuringSingleTextView.font = font;

    measuringTextView.frame = CGRectMake(0, 0, width, 500);
    measuringSingleTextView.frame = CGRectMake(0, 0, 50000, 500);
}

@end

0

我也曾经遇到过这个问题。通常情况下,我会将sizeWithFont方法传递一个比textView实际尺寸小的尺寸。即使将textview的内容插图添加到尺寸中,它也不起作用。返回的尺寸总是较小的。

  • 查理

0
你可以尝试检查UIScrollView的属性contentInset的值。

-1

我使用以下函数没有太多问题:

+(float) calculateHeightOfTextFromWidth:(NSString*) text: (UIFont*)withFont: (float)width :(UILineBreakMode)lineBreakMode

{
    [text retain];
    [withFont retain];
    CGSize suggestedSize = [text sizeWithFont:withFont constrainedToSize:CGSizeMake(width, FLT_MAX) lineBreakMode:lineBreakMode];

[text release];
[withFont release];

return suggestedSize.height;
}

那对我肯定不起作用。你是在处理UITextView还是其他什么东西? - Ashoat
我将这个应用于label上,但我相信在textview的情况下也会起作用。我调用这个类方法如下: float textHeight = [class_name_where_this_method_resides calculateHeightOfTextFromWidth:_content : content.font :content.frame.size.width :UILineBreakModeWordWrap]; 其中_content是我的实际文本,而content是我的标签名称。 - neha
我也遇到了UILabel的同样问题。 - tomwhipple
建议的解决方案不适用于UITextViews。返回的高度过小。 - Eric

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