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

添加多属性后NSAttributedString的backgroundColor不连续如何解决?

问题原因

这个空隙是UILabel默认渲染富文本的机制导致的:系统会为每一组属性完全相同的文本段单独绘制背景色,当相邻文本段的字号不同时,两段背景的上下高度、对齐位置不匹配,就会在分界处露出底层的label背景色,看起来就是竖线空隙。

修复方案

方案1:统一调整背景色段的基线偏移(轻量适配,改动最小)

给所有带背景色的文本段设置统一的基线偏移,让不同字号的文本背景高度和对齐位置完全一致,即可消除空隙。修改后的代码如下:

import PlaygroundSupport
import UIKit

let label = UILabel()
label.backgroundColor = .black
let text = "Hello + World!"
let nsText = NSString(string: text)

let attributedString = NSMutableAttributedString(string: text)
// 先设置全局的背景色,后续不同属性段再补全
let fullBgRange = nsText.range(of: "Hello + Wo")
attributedString.addAttribute(.backgroundColor, value: UIColor.systemCyan, range: fullBgRange)

let helloRange = nsText.range(of: "Hello")
let symbolRange = nsText.range(of: " + ")
let woRange = NSRange(location: symbolRange.upperBound, length: 2) // 对应"Wo"的范围

// 最大字号作为基准
let maxFontSize: CGFloat = 100
let maxFont = UIFont.systemFont(ofSize: maxFontSize)

attributedString.addAttribute(.font, value: maxFont, range: helloRange)
// 给小字号段设置基线偏移,对齐最大字号的基线
let symbolFont = UIFont.systemFont(ofSize: 80.0)
let symbolOffset = (maxFont.capHeight - symbolFont.capHeight) / 2
attributedString.addAttributes([
    .foregroundColor: UIColor.systemPurple,
    .font: symbolFont,
    .baselineOffset: symbolOffset
], range: symbolRange)

let woFont = UIFont.systemFont(ofSize: 44.0)
let woOffset = (maxFont.capHeight - woFont.capHeight) / 2
attributedString.addAttributes([
    .font: woFont,
    .baselineOffset: woOffset,
    .foregroundColor: UIColor.white
], range: woRange)

// 剩余"rld!"段设置
let worldRange = nsText.range(of: "World!")
let remainingWorldRange = NSRange(location: woRange.upperBound, length: worldRange.length - 2)
attributedString.addAttributes([
    .foregroundColor: UIColor.white,
    .font: woFont
], range: remainingWorldRange)

label.attributedText = attributedString
label.sizeToFit()

PlaygroundPage.current.liveView = label

方案2:自定义UILabel统一绘制背景(彻底解决无副作用)

如果不想调整文本基线,可以自定义UILabel子类,先遍历富文本的背景色范围,计算所有需要绘制背景的区域统一绘制,再渲染文本,从根源避免分段绘制背景的空隙问题,代码示例如下:

class BgAttributedLabel: UILabel {
    override func drawText(in rect: CGRect) {
        guard let attributedText = attributedText else {
            super.drawText(in: rect)
            return
        }
        // 先绘制所有背景色
        let textRange = NSRange(location: 0, length: attributedText.length)
        attributedText.enumerateAttribute(.backgroundColor, in: textRange, options: []) { value, range, _ in
            guard let color = value as? UIColor else { return }
            // 用CoreText计算当前range的文本位置
            let layoutManager = NSLayoutManager()
            let textContainer = NSTextContainer(size: bounds.size)
            textContainer.lineFragmentPadding = 0
            textContainer.maximumNumberOfLines = numberOfLines
            textContainer.lineBreakMode = lineBreakMode
            let textStorage = NSTextStorage(attributedString: attributedText)
            textStorage.addLayoutManager(layoutManager)
            layoutManager.addTextContainer(textContainer)
            let glyphRange = layoutManager.glyphRange(forCharacterRange: range, actualCharacterRange: nil)
            let bgRect = layoutManager.boundingRect(forGlyphRange: glyphRange, in: textContainer)
            color.setFill()
            UIRectFill(bgRect)
        }
        // 再绘制文本
        super.drawText(in: rect)
    }
}

使用时把原来的UILabel替换为BgAttributedLabel,原有富文本设置代码不需要修改即可生效。

内容的提问来源于stack exchange,提问作者pacification

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 05:06:03