添加多属性后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
相关产品推荐
相关产品推荐

