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

如何在NSTextView中正确为指定字符设置圆角矩形背景色?

实现NSTextView中带圆角背景的Tag文本

嘿,我来帮你搞定这个给特定字符加圆角矩形背景的需求!你已经找对了方向——重写NSLayoutManager的fillBackgroundRectArray方法是实现自定义背景绘制的关键,下面是完整的实现方案:

1. 自定义NSLayoutManager子类

我们需要在这个子类里识别带tag属性的文本范围,然后替换默认的矩形背景为圆角背景:

class TagLayoutManager: NSLayoutManager {
    override func fillBackgroundRectArray(_ rectArray: UnsafePointer<NSRect>, count rectCount: Int, forCharacterRange charRange: NSRange, color: NSColor) {
        guard let textStorage = self.textStorage else {
            // 如果没有textStorage, fallback到父类的默认实现
            super.fillBackgroundRectArray(rectArray, count: rectCount, forCharacterRange: charRange, color: color)
            return
        }
        
        var currentProcessingRange = charRange
        
        // 遍历当前字符范围内的所有属性块
        while currentProcessingRange.length > 0 {
            var effectiveAttributeRange = NSRange()
            let attributes = textStorage.attributes(at: currentProcessingRange.location, effectiveRange: &effectiveAttributeRange)
            
            // 只处理当前字符范围内的属性块交集
            let targetRange = NSIntersectionRange(currentProcessingRange, effectiveAttributeRange)
            
            if attributes["tag"] != nil {
                // 对带tag属性的文本,绘制圆角背景
                let cornerRadius = rectArray[0].height * 0.4 // 圆角半径设为文本高度的40%,比例更协调
                for index in 0..<rectCount {
                    let rect = rectArray[index]
                    let roundedPath = NSBezierPath(roundedRect: rect, xRadius: cornerRadius, yRadius: cornerRadius)
                    color.setFill()
                    roundedPath.fill()
                }
            } else {
                // 非tag文本,调用父类方法绘制默认背景
                super.fillBackgroundRectArray(rectArray, count: rectCount, forCharacterRange: targetRange, color: color)
            }
            
            // 移动到下一个属性块继续处理
            currentProcessingRange.location = NSMaxRange(effectiveAttributeRange)
            currentProcessingRange.length = charRange.length - (currentProcessingRange.location - charRange.location)
        }
    }
}

2. 配置NSTextView使用自定义LayoutManager

创建NSTextView时,需要把自定义的TagLayoutManager关联到它的文本系统中:

// 构建文本系统三组件:TextStorage -> LayoutManager -> TextContainer
let textStorage = NSTextStorage()
let tagLayoutManager = TagLayoutManager()
let textContainer = NSTextContainer(size: NSSize(width: 350, height: CGFloat.greatestFiniteMagnitude))

tagLayoutManager.addTextContainer(textContainer)
textStorage.addLayoutManager(tagLayoutManager)

// 创建NSTextView
let textView = NSTextView(frame: NSRect(x: 20, y: 20, width: 350, height: 250), textContainer: textContainer)
textView.isEditable = false // 根据你的需求设置是否可编辑
textView.drawsBackground = true

3. 添加带Tag属性的文本

给需要标记的文本添加tag属性和背景色,这样自定义LayoutManager就会识别并绘制圆角:

let mutableAttributedString = NSMutableAttributedString(string: "这是一段普通文本,[重要标签1] 这又是普通文本,[重要标签2]")

// 给"[重要标签1]"添加tag属性
let tagRange1 = (mutableAttributedString.string as NSString).range(of: "[重要标签1]")
mutableAttributedString.addAttribute("tag", value: true, range: tagRange1)
mutableAttributedString.addAttribute(NSBackgroundColorAttributeName, value: NSColor.systemOrange.withAlphaComponent(0.3), range: tagRange1)

// 给"[重要标签2]"添加tag属性
let tagRange2 = (mutableAttributedString.string as NSString).range(of: "[重要标签2]")
mutableAttributedString.addAttribute("tag", value: true, range: tagRange2)
mutableAttributedString.addAttribute(NSBackgroundColorAttributeName, value: NSColor.systemBlue.withAlphaComponent(0.3), range: tagRange2)

// 设置到textView的textStorage中
textStorage.setAttributedString(mutableAttributedString)

关键注意点

  • 圆角自适应:我们用文本高度的40%作为圆角半径,这样不同字号的tag文本都能保持协调的圆角比例。
  • 换行处理:如果tag文本换行,rectArray会包含多个换行后的rect,每个都会被绘制圆角,保证换行后的每一段tag文本都有完整的圆角背景。
  • 性能优化:通过遍历属性块而非单个字符,避免不必要的重复绘制,保证文本较多时的性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 03:36:58