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

为何NSAttributedString在NSRulerView中随NSTextView滚动,NSRect却不?

问题分析与解决方案

这个问题我之前也碰到过,核心原因是NSRulerView中绘制NSRect时没有结合文本视图的滚动偏移和坐标系转换,而行号文本的绘制逻辑已经适配了滚动,所以才会出现两者行为不一致的情况。

为什么行号文本会跟随滚动,而NSRect不会?

1. 行号文本的绘制逻辑适配了滚动

你在绘制行号时,应该是通过NSTextView的layoutManager获取当前可见区域对应的行,然后根据每行的lineFragmentRect计算在NSRulerView中的位置。这个过程中,你会自动结合文本视图的documentVisibleRect(可见区域)的偏移量,所以每次滚动触发NSRulerView重绘时,行号的位置都会重新计算,自然会跟着滚动走。

2. 手动绘制的NSRect未考虑滚动偏移

如果你的NSRect是基于某个固定的y值绘制,或者没有将文本行的位置转换到NSRulerView的坐标系中,它就会固定在NSRulerView的静态bounds位置里,不会随文本视图的滚动更新位置。

举个反例:如果直接写这样的代码,背景Rect肯定不会滚动:

// 错误示例:固定y值,未考虑滚动
let badBackgroundRect = NSRect(x: 0, y: 50, width: GUTTER_WIDTH, height: 20)
badBackgroundRect.fill()

解决方法:动态计算选中行背景Rect的位置

你需要把选中行在文本视图中的位置,转换到NSRulerView的坐标系中,同时结合文本视图的滚动偏移,才能让背景Rect跟随滚动。

修改你的绘制代码,参考以下逻辑:

override func drawHashMarksAndLabels(in rect: NSRect) {
    super.drawHashMarksAndLabels(in: rect)
    
    guard let textView = clientView as? NSTextView else { return }
    let layoutManager = textView.layoutManager
    
    // 1. 获取选中范围对应的行
    let selectedRange = textView.selectedRange()
    let lineRange = layoutManager.lineRange(for: selectedRange)
    
    // 2. 获取该行在文本视图中的frame
    let lineRect = layoutManager.lineFragmentRect(forGlyphAt: lineRange.location, effectiveRange: nil)
    
    // 3. 将文本视图中的行frame转换到NSRulerView的坐标系
    let convertedRect = textView.convert(lineRect, to: self)
    
    // 4. 绘制选中行的背景Rect(宽度用你的GUTTER_WIDTH)
    let backgroundRect = NSRect(
        x: 0,
        y: convertedRect.origin.y,
        width: GUTTER_WIDTH,
        height: convertedRect.height
    )
    
    // 设置背景色并填充
    NSColor.systemBlue.withAlphaComponent(0.2).setFill()
    backgroundRect.fill()
    
    // 后续绘制行号文本的逻辑保持不变...
}

关键知识点

  • macOS的坐标系是左下角为原点,所以转换坐标系时要注意视图层级的位置关系。
  • convert(_:to:)方法会帮你自动处理不同视图之间的坐标偏移,包括滚动带来的位置变化。
  • NSRulerView的drawHashMarksAndLabels(in:)会在文本视图滚动、内容变化时自动触发重绘,所以只要每次重绘时都动态计算位置,就能保证背景Rect和行号同步滚动。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 11:13:45