NSTextView select 具体行

NSTextView select specific line

我在 Xcode 10,Objective-C,macOS 不是 iOS。

如果给出了行号,是否可以通过编程方式select NSTextView 中的一行?不是通过更改内容的任何属性,只是 select 就像用户通过三次单击那样。

我知道如何按范围获取 selected 文本,但这次我需要以编程方式 select 文本。

我找到了 selectLine:(id),但它似乎是一个插入点。 指向正确方向的指针将非常有用,非常感谢。

此处 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TextLayout/Tasks/CountLines.html 的 Apple 文档应该对您尝试执行的操作有用。

在计算换行文本行数的示例中,他们使用 NSLayoutManager 方法 lineFragmentRectForGlyphAtIndex:effectiveRange https://developer.apple.com/documentation/appkit/nslayoutmanager/1403140-linefragmentrectforglyphatindex 找到行的有效范围,然后将计数索引增加到该范围的末尾(即从下一行开始)。通过一些小的修改,你可以使用它来找到你想要的行的范围select,然后使用NSTextView的setSelectedRange:到select它。

这里修改为我认为它可能对您试图完成的工作有用的地方:

- (void)selectLineNumber:(NSUInteger)lineNumberToSelect {
    NSLayoutManager *layoutManager = [self.testTextView layoutManager];
    NSUInteger numberOfLines = 0;
    NSUInteger numberOfGlyphs = [layoutManager numberOfGlyphs];
    NSRange lineRange;
    for (NSUInteger indexOfGlyph = 0; indexOfGlyph < numberOfGlyphs; numberOfLines++) {
        [layoutManager lineFragmentRectForGlyphAtIndex:indexOfGlyph effectiveRange:&lineRange];
        // check if we've found our line number
        if (numberOfLines == lineNumberToSelect) {
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                [self.testTextView setSelectedRange:lineRange];
            }];
            break;
        }
        indexOfGlyph = NSMaxRange(lineRange);
    }
}

然后你可以这样调用它:

[self selectLineNumber:3];

请记住,我们从索引 0 开始。如果您传入大于 numberOfLines 的 lineNumberToSelect,它应该只是一个空操作,selection 应该保留在它所在的位置是。

谢谢@R4N!您的回答帮助我弄清楚如何使用 NSTextView 跳转到特定行。我将你的 Obj-C 转换为 Swift 5:

func selectLineNumber(lineNumberToJumpTo: Int) {
    let layoutManager = textView.layoutManager!
    var numberOfLines = 1
    let numberOfGlyphs = layoutManager.numberOfGlyphs
    var lineRange: NSRange = NSRange()
    var indexOfGlyph = 0
    
    while indexOfGlyph < numberOfGlyphs {
      layoutManager.lineFragmentRect(forGlyphAt: indexOfGlyph, effectiveRange: &lineRange, withoutAdditionalLayout: false)
      // check if we've found our line number
      if numberOfLines == lineNumberToJumpTo {
        
        textView.selectedRange = lineRange
        textView.scrollRangeToVisible(lineRange)
        
        break
      }
      indexOfGlyph = NSMaxRange(lineRange)
      numberOfLines += 1
    }
  }