为何两个棕色UILabel总高度与单个红色UILabel高度不一致?
这个问题我之前也碰到过,核心是UILabel的文本排版逻辑和尺寸计算方式导致的,我来给你拆解一下:
一、为什么高度会有差异?
有两个关键因素:
1. 连续换行符的排版差异
当单个UILabel加载完整拼接字符串(\nfoo\n\nbar\n\nfoo\n\nbar\n)时,中间的两个连续换行符(第一个子串结尾的\n + 第二个子串开头的\n)会被TextKit合并成一个空行的高度。但拆成两个UILabel后,第一个Label结尾的\n会生成一个独立的空白行,第二个Label开头的\n也会生成另一个空白行——这两个空白行的高度会被分别计算,直接导致总高度比单个Label多了一个空白行的高度。
2. 尺寸计算的逻辑漏洞
你写的createLabel函数里,先调用sizeToFit()让Label自适应文本的自然宽度,再强制设置frame.size.width = viewWidth。这种做法的问题是:sizeToFit()计算的高度是基于文本的自然宽度(可能比viewWidth小),之后修改宽度并不会自动触发高度的重新计算,导致Label的高度并不是适配viewWidth的正确高度,进一步放大了差异。
二、不修改字符串的解决方法
我们可以从修正尺寸计算和调整排版样式两个方向入手,不需要修改切割后的字符串:
1. 先修正Label的尺寸计算逻辑
首先要保证Label的高度是严格基于viewWidth计算的,替换你的createLabel函数:
func createLabel(_ text: String, _ backgroundColor: UIColor = .white) -> UILabel { let label = UILabel() label.text = text label.numberOfLines = 0 label.backgroundColor = backgroundColor label.textColor = .black // 基于指定宽度计算精准的适配高度 let fittingSize = label.sizeThatFits(CGSize(width: viewWidth, height: .greatestFiniteMagnitude)) label.frame = CGRect(x: 0, y: 0, width: viewWidth, height: fittingSize.height) return label }
这个改动会让Label的高度完全适配viewWidth,避免尺寸计算的偏差。
2. 用段落样式抵消重复空白行
通过富文本的段落样式,我们可以让第一个Label的结尾换行和第二个Label的开头换行不产生重复的高度。修改createLabel函数,增加一个参数标记是否为最后一个Label:
func createLabel(_ text: String, _ backgroundColor: UIColor = .white, isFinalLabel: Bool = false) -> UILabel { let label = UILabel() label.numberOfLines = 0 label.backgroundColor = backgroundColor label.textColor = .black // 创建自定义段落样式 let paragraphStyle = NSMutableParagraphStyle() // 非最后一个Label:取消结尾的段落间距,避免和下一个Label的开头换行重复计算 if !isFinalLabel { paragraphStyle.paragraphSpacing = 0 } // 生成富文本赋值给Label let attributedText = NSAttributedString( string: text, attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle] ) label.attributedText = attributedText // 基于指定宽度计算高度 let fittingSize = label.sizeThatFits(CGSize(width: viewWidth, height: .greatestFiniteMagnitude)) label.frame = CGRect(x: 0, y: 0, width: viewWidth, height: fittingSize.height) return label }
然后创建Label时标记最后一个:
let label1 = createLabel("\nfoo\n\nbar\n", .green) let label2 = createLabel("\nfoo\n\nbar\n", .blue, isFinalLabel: true) let label3 = createLabel("\nfoo\n\nbar\n\nfoo\n\nbar\n", .red)
这个做法会抵消两个Label之间重复的空白行高度,让总高度和单个Label完全一致。
验证效果
修改后运行Playground,你会发现label1+label2的总高度和label3的高度完全相同,同时保留了原字符串的内容。
内容的提问来源于stack exchange,提问作者mownier

