iOS 16.4中NSAttributedString的baselineOffset异常问题求助
iOS 16.4中NSAttributedString的baselineOffset表现不一致问题解决方法
在iOS 16.4环境下编译项目后,设置了固定行高(maximumLineHeight/minimumLineHeight)与baselineOffset的NSAttributedString在UILabel中的显示效果会和iOS 16.0产生差异,这是因为苹果在iOS 16.4中调整了行高与基线偏移的组合计算逻辑。以下是几种可行的解决思路:
方法一:基于系统版本动态调整baselineOffset
iOS 16.4及以后,当设置固定行高时,系统会先将文本在指定行高内垂直居中,再应用基线偏移。我们可以通过计算行高与字体默认行高的差值,调整基线偏移的数值,让显示效果和旧版本一致:
private func generateFontAttr() -> [NSAttributedString.Key: Any] { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.maximumLineHeight = 120 paragraphStyle.minimumLineHeight = 120 let font = UIFont.systemFont(ofSize: 18) var baselineOffset: CGFloat = 20 // 适配iOS 16.4及以上版本 if #available(iOS 16.4, *) { // 计算行高与字体默认行高的差值,修正基线偏移 let lineHeightDiff = paragraphStyle.minimumLineHeight - font.lineHeight baselineOffset = 20 - lineHeightDiff / 2 } let attrs: [NSAttributedString.Key: Any] = [ .font: font, .paragraphStyle: paragraphStyle, .baselineOffset: baselineOffset ] return attrs }
方法二:改用lineSpacing替代固定行高(业务允许时)
如果不需要强制固定行高,可以移除maximumLineHeight和minimumLineHeight的设置,改用lineSpacing来控制行间距。这种方式下,基线偏移的表现会在不同系统版本中保持一致:
private func generateFontAttr() -> [NSAttributedString.Key: Any] { let font = UIFont.systemFont(ofSize: 18) let paragraphStyle = NSMutableParagraphStyle() // 计算需要的行间距,达到近似原固定行高的效果 paragraphStyle.lineSpacing = 120 - font.lineHeight let baselineOffset = 20 let attrs: [NSAttributedString.Key: Any] = [ .font: font, .paragraphStyle: paragraphStyle, .baselineOffset: baselineOffset ] return attrs }
方法三:自定义UILabel手动控制绘制位置
如果上述两种方法都不满足需求,可以自定义UILabel,重写文本绘制逻辑,绕过系统的行高计算,直接控制文本的显示位置:
class CustomLabel: UILabel { override func drawText(in rect: CGRect) { guard let attributedText = attributedText else { super.drawText(in: rect) return } let mutableAttr = NSMutableAttributedString(attributedString: attributedText) // 移除系统的baselineOffset设置,手动控制位置 mutableAttr.removeAttribute(.baselineOffset, range: NSRange(location: 0, length: mutableAttr.length)) // 计算文本的实际尺寸 let textRect = mutableAttr.boundingRect(with: CGSize(width: rect.width, height: .infinity), options: .usesLineFragmentOrigin, context: nil) // 手动设置偏移后的绘制位置(这里的20对应原baselineOffset值) let offsetY = rect.height - textRect.height + 20 let drawRect = CGRect(x: rect.origin.x, y: rect.origin.y + offsetY, width: rect.width, height: textRect.height) mutableAttr.draw(in: drawRect) } }
问题原因说明
iOS 16.4对NSParagraphStyle中固定行高与baselineOffset的组合逻辑进行了优化:旧版本中基线偏移是直接叠加在文本默认基线上;而iOS 16.4及以后,当设置固定行高时,系统会先将文本在指定行高范围内垂直居中,再应用基线偏移,这就导致了最终文本位置的差异。
内容的提问来源于stack exchange,提问作者Sajjad
相关产品推荐
相关产品推荐

