You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 02:37:50