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

如何获取UILabel内文本的x、y、宽高参数并实现逐行圆角背景?

为UILabel文本片段绘制跨行圆角背景的解决方案

一、正确计算文本片段的排版CGRect

UILabel的文本排版依赖NSLayoutManager和NSTextContainer,要获取每个文本片段的实际位置,需要直接和这些底层类交互:

  1. 提前记录需要添加背景的文本片段对应的NSRange,或给富文本片段标记自定义属性
  2. 从UILabel中取出NSLayoutManager、NSTextContainer实例
  3. 遍历目标文本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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 08:13:21