如何获取UILabel内文本的x、y、宽高参数并实现逐行圆角背景?
为UILabel文本片段绘制跨行圆角背景的解决方案
一、正确计算文本片段的排版CGRect
UILabel的文本排版依赖NSLayoutManager和NSTextContainer,要获取每个文本片段的实际位置,需要直接和这些底层类交互:
- 提前记录需要添加背景的文本片段对应的
NSRange,或给富文本片段标记自定义属性 - 从UILabel中取出
NSLayoutManager、NSTextContainer实例 - 遍历目标文本range,通过
layoutManager的枚举方法获取该range覆盖的所有行的CGRect,转换为Label坐标系下的矩形后收集
核心代码片段:
// 假设已定义需要处理的文本片段range集合:targetRanges guard let layoutManager = self.layoutManager, let textContainer = self.textContainer else { return } for range in targetRanges { var glyphRange = NSRange() layoutManager.characterRange(forGlyphRange: range, actualGlyphRange: &glyphRange) var lineRects = [CGRect]() // 枚举该文本片段覆盖的所有行 layoutManager.enumerateLineFragments(forGlyphRange: glyphRange) { lineRect, _, _, _, _ in let convertedRect = self.convert(lineRect, from: textContainer) // 可根据需求调整矩形内边距 let adjustedRect = convertedRect.inset(by: UIEdgeInsets(top: 2, left: 4, bottom: 2, right: 4)) lineRects.append(adjustedRect) } // 用RectangleContour生成圆角路径并绘制 let contour = lineRects.contour() let roundedPath = contour.cgPath(cornerRadius: customCornerRadius ?? 6) // 绘制路径逻辑... }
注意:必须确保UILabel执行过layoutIfNeeded()后再计算矩形,否则获取的位置会不准确。
二、替代库推荐
如果觉得直接操作排版类太繁琐,可以考虑这些现成库:
- YYText:提供
YYLabel组件,支持给富文本设置YYTextBackgroundBorder属性,一键实现跨行圆角背景,无需手动计算矩形。 - TTRichLabel:轻量型富文本Label,支持自定义文本背景,API简洁,适配跨行圆角场景。
- iOS15+ SwiftUI Text:若项目适配SwiftUI,可通过分段
Text结合.background(RoundedRectangle(cornerRadius: x))实现类似效果,无需额外库。
三、完整自定义Label示例
import UIKit import RectangleContour class CustomBackgroundLabel: UILabel { var customCornerRadius: CGFloat = 6 var customBackgroundColor: UIColor = UIColor(red: 94/255, green: 17/255, blue: 82/255, alpha: 0.58) // 存储需要添加背景的文本片段range var targetTextRanges: [NSRange] = [] override func draw(_ rect: CGRect) { super.draw(rect) guard let layoutManager = layoutManager, let textContainer = textContainer, let attributedText = attributedText else { return } let context = UIGraphicsGetCurrentContext()! context.setFillColor(customBackgroundColor.cgColor) for range in targetTextRanges { // 校验range有效性 guard range.location != NSNotFound, range.length > 0, NSLocationInRange(range.location, attributedText.string.nsRange) else { continue } var glyphRange = NSRange() layoutManager.characterRange(forGlyphRange: range, actualGlyphRange: &glyphRange) var lineRects = [CGRect]() layoutManager.enumerateLineFragments(forGlyphRange: glyphRange) { lineRect, _, _, _, _ in let convertedRect = self.convert(lineRect, from: textContainer) let adjustedRect = convertedRect.inset(by: UIEdgeInsets(top: 2, left: 4, bottom: 2, right: 4)) lineRects.append(adjustedRect) } let contour = lineRects.contour() let roundedPath = contour.cgPath(cornerRadius: customCornerRadius) context.addPath(roundedPath) context.fillPath() } } override func layoutSubviews() { super.layoutSubviews() setNeedsDisplay() // 布局变化时重新绘制背景 } } // 使用示例 let label = CustomBackgroundLabel() label.text = "这是一段需要跨行的文本片段,测试背景效果,看看是否能正确覆盖每一行" label.numberOfLines = 0 label.font = UIFont.systemFont(ofSize: 16) // 给前10个字符添加背景 label.targetTextRanges = [NSRange(location: 0, length: 10)]
内容的提问来源于stack exchange,提问作者Pql
相关产品推荐
相关产品推荐

