views:

102

answers:

1

I am using the code I posted as an answer to this question to display hidden characters in an NSTextView as bullets.

I am coloring the bullets gray with the addTemporaryAttributes:forCharacterRange: method of NSLayoutManager. Each range is of length 1 and colors a single bullet. (Most of the same text I am using has relatively few adjacent hidden characters, although this is an optimization I am planning to make in the future.)

This change has many adverse effects. Scrolling the text view becomes slow and jerky, especially for text with many hidden characters. How can I color parts of the text without incurring an unacceptable performance hit?

+2  A: 

The reason the biggest hit is coming when you scroll is that, every time the scrollview scrolls, it redraws the view and, by extension, recolorizes your entire document. Since, during a single scroll operation, the scrollview is scrolling by several pixels, just scrolling n pixels up or down is going to recolorize the entire document n times!

I suggest making sure you are only colorizing text that's on-screen. The last thing you ever want to be doing is adding attributes to characters that have been scrolled out of view, since it's essentially wasted cycles at that point.

You can get the range of visible text from the text view:

- (NSRange)visibleRange
{
    NSRect visibleRect = [textView visibleRect];

    NSRange glyphVisibleRange = [[textView layoutManager] glyphRangeForBoundingRect:visibleRect inTextContainer:[textView textContainer]];
    NSRange charVisibleRange = [[textView layoutManager] characterRangeForGlyphRange:glyphVisibleRange  actualGlyphRange:nil];
    return charVisibleRange;
}

So, when you do your colorizing, only colorize glyphs that fall within this range. That should improve your scrolling performance by several orders of magnitude.

Matt Ball
Why would the first argument of -drawGlyphsForGlyphRange:atPoint: not be the visible range?
titaniumdecoy
I think this actually slows the process down; apparently -drawGlyphsForGlyphRange:atPoint: returns only the range that needs to be redrawn which is not necessarily the entire visible range. (Although I could be mistaken about this.)
titaniumdecoy