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

如何设置NSTextView仅可点击链接、不可选中其他普通文本

NSTextView实现链接可点击、普通文本不可选中的解决方案

直接设置isSelectable = false会拦截所有鼠标事件包括链接点击,官方推荐的选择变更委托方法会出现先选中再取消的闪烁问题,可通过以下两种方案实现需求:

方案1:属性字符串分段配置(优先推荐)

AppKit 从OS X 10.11开始支持NSAttributedString.Key.selectable属性,可针对富文本的不同片段单独设置是否可选,无需修改NSTextView全局属性:

  • 解析HTML生成NSMutableAttributedString后,遍历整个文本范围
  • 对没有携带NSAttributedString.Key.link属性的普通文本片段,添加.selectable属性值为false
  • 对携带链接属性的片段,保留.selectable属性值为true
  • 保持NSTextView的isSelectable属性为默认的true即可

该方案代码量最低,完全不会出现选中闪烁的问题。

方案2:自定义NSTextView子类(兼容低版本系统)

如果需要适配10.11以下的系统版本,可以通过自定义子类拦截选中逻辑实现需求:

class LinkOnlyTextView: NSTextView {
    // 重载鼠标按下事件,拦截非链接区域的点击
    override func mouseDown(with event: NSEvent) {
        let clickLocation = convert(event.locationInWindow, from: nil)
        let characterIndex = characterIndexForInsertion(at: clickLocation)
        
        guard characterIndex < textStorage?.length ?? 0,
              textStorage?.attribute(.link, at: characterIndex, effectiveRange: nil) != nil else {
            return
        }
        super.mouseDown(with: event)
    }
    
    // 重载选中范围变更方法,彻底拦截非链接区域的选中
    override func setSelectedRanges(_ ranges: [NSValue], affinity: NSSelectionAffinity, stillSelecting stillSelectingFlag: Bool) {
        let validRanges = ranges.filter { range in
            guard let textStorage = textStorage else { return false }
            var hasLink = false
            textStorage.enumerateAttribute(.link, in: range.rangeValue) { value, _, stop in
                if value != nil {
                    hasLink = true
                    stop.pointee = true
                }
            }
            return hasLink
        }
        super.setSelectedRanges(validRanges, affinity: affinity, stillSelecting: stillSelectingFlag)
    }
}

使用说明

  • 替换原有的NSTextView实例为自定义的LinkOnlyTextView类型
  • 保持isSelectable属性为默认的true即可
  • 不需要实现官方的选择变更委托方法,彻底避免选中后再取消的闪烁问题
  • 鼠标点击非链接区域时会直接拦截事件,不会触发任何选中逻辑,点击链接区域时保留默认的跳转响应

如需自定义链接点击后的处理逻辑,可以实现NSTextViewDelegate的如下方法:

func textView(_ textView: NSTextView, clickedOnLink link: Any, at charIndex: Int) -> Bool {
    guard let url = link as? URL else { return false }
    // 自定义URL处理逻辑
    NSWorkspace.shared.open(url)
    return true
}

内容的提问来源于stack exchange,提问作者Антон Озерянский

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 06:18:05