NSTextView select specific line

2019-08-18 03:37发布

问题:

I am on Xcode 10, Objective-C, macOS NOT iOS.

Is it possible to programmatically select a line in NSTextView if the line number is given? Not by changing any of the attributes of the content, just select it as a user would do it by triple clicking.

I know how to get selected text by its range but this time i need to select text programmatically.

I've found selectLine:(id) but it seems to be for an insertion point. A pointer to the right direction would be great and very much appreciated.

回答1:

The Apple documentation here https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/TextLayout/Tasks/CountLines.html should be useful for what you're attempting to do.

In their example of counting lines of wrapped text they use the NSLayoutManager method lineFragmentRectForGlyphAtIndex:effectiveRange https://developer.apple.com/documentation/appkit/nslayoutmanager/1403140-linefragmentrectforglyphatindex to find the effective range of the line, then increase the counting index to the end of that range (i.e. starting at the next line). With some minor modification, you can use it to find the range of the line you'd like to select, then use NSTextView's setSelectedRange: to select it.

Here's it modified to where I believe it would probably work for what you're attempting to accomplish:

- (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);
    }
}

Then you could call it with something like:

[self selectLineNumber:3];

Keep in mind that we're starting at index 0. If you pass in a lineNumberToSelect that is greater than the numberOfLines, it should just be a no-op and the selection should remain where it is.