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

时间:2010-08-31 19:29:26

标签: iphone objective-c cocoa-touch uitextview

我正在使用方法[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。

我的问题:有没有办法以编程方式实际检索此值,或者是否有一些我错过了指定此编号的文档?似乎这个数字应该是可用的,不应该被猜测和检查,但我找不到可靠的方法来识别它。

提前致谢。

6 个答案:

答案 0 :(得分:6)

好的,经过一些(重新)搜索后,我来到了这里。

UITextView每边都有8px的填充。但是UILabel(和sizeWithFont方法的结果)

的行高也不一样

在我的情况下,字体是Arial,大小是16pt,textview的行高比uilabel的行高高4.5个点。所以;

  • 我从 sizeWithFont 方法获得结果( cSize ),宽度减少16像素
  • 我通过 cSize.height / font.lineHeight
  • 计算了 lineCount
  • 并使用 cSize.height +(lineCount * 4.5)+ 16.0 作为textview的最终高度

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

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

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

答案 1 :(得分: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;

结果应该是正确的。

答案 2 :(得分:3)

使用Core Text计算

NSString sizeWithFontUITextView使用Webview(内部)呈现其内容。

这种差异会导致各种令人头疼的问题。比如选择在不同位置断行 - 你会注意到逗号分隔的列表,并且某些数学表达式字符串将在Core Text和UITextView之间的不同符号上中断。在测量弦的空间以适应时,这通常会导致一线错误。

如果您想要国际角色支持 - 那么,您可以忘记与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

答案 3 :(得分:0)

您可以尝试检查contentInset值(UIScrollView的属性)。

答案 4 :(得分:0)

我也在努力解决这个问题。我通常通过传递sizeWithFont方法获得的大小小于textView的实际大小。即使将textview的内容插入添加到大小,它也不起作用。返回的大小总是较小。

  • 查理

答案 5 :(得分:-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;
}