关于UILabel高度计算错误问题的解决方案

关于开发中一些问题的记录。

如下图所示,《活动:一起聊聊动物世界》这个信息流Cell的标题,本应换行的Label内容直接被截断了,并且上下空白太多。

Debug

  • 首先尝试在不同系统同一机型上进行Debug,发现4.7英寸屏幕从iOS8到iOS12.1都未出现该问题,暂时排除系统版本导致Bug。
  • 尝试不同机型,发现5.5英寸和5.8英寸屏都出现了内容被截断的现象,由于界面采用比例布局,初步判定是在动态计算Label高度时出现的精度问题(float类型在计算时会四舍五入)
  • 尝试在界面layoutSubview以后,对Label进行sizeToFit操作,结果如图

  • 看起来成功了,但这样的操作似乎是运气好,实际上是存在问题的,此时Cell的高度已经确定,我们只是强制改变了标题Label的frame,而Cell的高度并没有改变。并且如果Label添加了autolayout约束,那么sizeToFit会失效
  • 优化为使用sizeThatFits拿到最优布局信息后更新约束,这样信息流Cell的自适应高度也会更新为理想高度
1
2
3
4
CGSize labelSize = [self.titleLabel sizeThatFits:CGSizeMake(self.titleLabel.frame.size.width, MAXFLOAT)];
[self.titleLabel mas_updateConstraints:^(MASConstraintMaker *make) {
make.height.mas_equalTo(labelSize.height);
}];

问题

  • 猜测系统UILabel进行布局时,使用了NSString的boundingRectWithSize:方法。在计算出排版高度时,由于是float类型可能存在四舍五入等情况,最后存在的误差导致UILabel显示不全,上下空白太多
  • 网络上有一些解决方案,例如boundingRectWithSize:计算出结果后取整再+1,这样的做法个人认为不够优雅,它并不是UILabel渲染的理想值,推荐使用sizeThatFits:

关于sizeThatFits和sizeToFit

  • sizeToFit方法会计算最适合内容的宽度和高度,并会直接作用于调用对象frame
  • sizeThatFits:方法会计算最适合内容的宽度和高度,但是不会直接作用于对象
  • sizeThatFits:方法计算的值是理想值