UILabel的sizeToFit/sizeThatFits忽略了numberOfLines属性

41

问题:假设宽度为300像素,确定一个 UILabel 所需的大小(行数)。由于字符串较长,我将 lineBreakMode 设置为 UILineBreakModeWordWrap 并调用 sizeThatFits 来尝试确定大小。但是,它在单行中给出了457像素的宽度,而不是预期的两行中的300像素。

请参见:

CGSize available = CGSizeMake(300, INFINITY);
UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, 400)] autorelease];
label.text = title;
label.lineBreakMode = UILineBreakModeWordWrap;
label.font = [UIFont fontWithName:kBoldFont size:kTitleFontSize];
label.numberOfLines = 3;
CGSize sizedtoFit = [label sizeThatFits:available];

但我发现sizedtoFit变量的宽度为457像素,高度为22像素,并且UI显示一行被裁剪的文本。我期望得到的是宽度为300像素,高度为44像素,可以显示两行。

UILabel文档中关于numberoflines的说明:

当使用sizeToFit方法对接收者进行调整大小时,调整大小考虑此属性中存储的值。例如,如果将此属性设置为3,则sizeToFit方法会调整接收者的大小,使其足够大以显示三行文本。

我尝试了各种组合:

  1. 将CGRectZero传递给init函数,传递300x400或300 x infinity。
  2. 创建后设置frame而不是将其传递给init函数。
  3. 调用[sizeToFit]并希望它计算假定当前宽度的高度,但它没有。
  4. 调用sizeToFit,然后调用sizeThatFits`。
  5. 调用layoutIfNeeded。

它们都没有奏效。我做错了什么,还是这是文档和框架实现不一致的bug?谢谢。


根据文档,我认为这是一个错误,并且我已经报告了它。 - an0
3
这个问题在iOS6中似乎被解决了——[sizeToFit]现在会考虑到numberOfLines。 - Pavel Alexeev
@Pavel 是正确的,我刚从5.1升级到6.0,神奇的是sizeToFit返回了正确的尺寸(高度增加了)。 - Kirk Woll
6个回答

29

我遇到了同样的问题,size that fits方法忽略了大小... /: 我最终使用了:

CGRect textSize = [UILabel textRectForBounds:CGRectMake(0, 0, 300, CGFLOAT_MAX) 
                      limitedToNumberOfLines:3];

非常顺利... :)

文档说你不应该直接调用它,但我已经使用它有一段时间了,通过已批准的提交的应用程序,一切都很棒... :)


2
除非没有其他办法,否则我不愿意这样做 :) - Kartick Vaddadi
@KartickVaddadi,您能详细说明一下吗? :) - Alex Zak
8
好的,我不想做文档中说不应该做的事情,除非是最后的选择。 - Kartick Vaddadi
2
这是否假定您正在使用默认字体? - CharlieMezak
3
目前的文档似乎可以使用实例方法 CGRect textSize = [label textRectForBounds:CGRectMake(0, 0, 300, CGFLOAT_MAX) limitedToNumberOfLines:3]。注意:这不是类方法,与 @AlexZak 的答案不同。参考资料:https://developer.apple.com/library/ios/documentation/uikit/reference/UILabel_Class/Reference/UILabel.html#//apple_ref/occ/instm/UILabel/textRectForBounds:limitedToNumberOfLines: - Anthony Mills
UITextView在iOS 8中存在同样的bug,因此当涉及到UITextView时,我不得不求助于您的答案。 - DawnSong

7

您尝试过sizeWithFont: constrainedToSize: lineBreakMode:方法吗?

例如:

CGSize sizeToFit = [title sizeWithFont:label.font constrainedToSize:label.frame.size lineBreakMode:label.lineBreakMode];

1
那是我的备选方案,但问题是:为什么sizeThatFits没有按照预期工作?我刚刚被告知,如果将numberOfLines设置为0(无限制),它就可以正常工作。 - Kartick Vaddadi
如果你想要三行的最大高度,那么这种方法就不好用了,就像问题所暗示的那样。我猜我需要计算三行的高度并将其传递给sizeWithFont函数。这很糟糕。 - mxcl
很棒的答案。我发现这对于多行标签最有用。但是,sizeWithFont在iOS7中已经被弃用了。请看一下我的单独答案,使用boundingRectWithSize:。 - auco

3

我发现Ian L的答案最好,他使用-sizeWithFont:constrainedToSize:lineBreakMode:,不幸的是,在iOS7下,sizeWithFont:已经被废弃了。

这是在iOS7中,对于UILabel子类,sizeWithFont:的工作原理:

NSRange range = NSMakeRange(0, self.attributedText.length); 
sizeToFit = [self.text boundingRectWithSize:self.bounds.size
                                    options:NSStringDrawingUsesLineFragmentOrigin 
                                 attributes:[self.attributedText 
                          attributesAtIndex:0 effectiveRange:&range] context:nil].size;

1

这已经全部过时了。请使用boundingRectWithSize。


0

我认为你得到了意外的结果,因为你没有考虑到 UILabel 的字体。请尝试以下操作:

UILabel *label = [[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, 400)] autorelease];
label.text = title;
label.lineBreakMode = UILineBreakModeWordWrap;
label.font = [UIFont fontWithName:kBoldFont size:kTitleFontSize];
label.numberOfLines = 0;

CGSize size = [label.text sizeWithFont:label.font constrainedToSize:CGSizeMake(label.frame.size.width,FLT_MAX) lineBreakMode:UILineBreakModeWordWrap ];
label.frame = CGRectMake(label.frame.origin.x,label.frame.origin.y,label.frame.size.width,size.height);

此解决方案不限制行数。 - titaniumdecoy
除了不限制行数外,它还使用NSString的尺寸方法,这将忽略UILabel的许多其他功能。 - pottedmeat

0

对于 sizeToFit,ios5 没有解决方案。您可以使用其他解决方案,例如 sizeWithFont 等。在 ios6 中,该问题已得到解决。但是,我有一个解决方法:

int lineCount = myLabel.numberOfLines;
myLabel.numberOfLines = 0;
[myLabel sizeToFit];
myLabel.numberOfLines = lineCount;

它可以工作。请注意,对于我的情况,我的标签宽度是固定的,我只需要使用sizeToFit来调整高度。


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