UITextView attributedText and syntax highlighting

2019-02-04 00:20发布

Background

So, with iOS 6 an UITextView can take an attributedString, which could be useful for Syntax highlighting.

I'm doing some regex patterns in -textView:shouldChangeTextInRange:replacementText: and oftentimes I need to change the color of a word already typed. I see no other options than resetting the attributedText, which takes time.

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
    //A context will allow us to not call -attributedText on the textView, which is slow.
    //Keep context up to date
    [self.context replaceCharactersInRange:range withAttributedString:[[NSAttributedString alloc] initWithString:text attributes:self.textView.typingAttributes]];

    // […]

    self.textView.scrollEnabled = FALSE;

    [self.context setAttributes:self.defaultStyle range:NSMakeRange(0, self.context.length)];
    [self refresh]; //Runs regex-patterns in the context
    textView.attributedText = self.context;

    self.textView.selectedRange = NSMakeRange(range.location + text.length, 0);
    self.textView.scrollEnabled = TRUE;

    return FALSE;
}

This runs okayish on the simulator, but on an iPad 3 each -setAttributedText takes a few hundreds of milliseconds.

I filed a bug to Apple, with the request of being able to mutate the attributedText. It got marked as a duplicate, so I cannot see what they're saying about this.

The question

The more specific question: How can I change the color of certain ranges in a UITextView, with a large multicolored text, with good enough performance to do it in every shouldReplaceText...?

The more broad question: How do you do syntax highlighting with a UITextView in iOS 6?

2条回答
太酷不给撩
2楼-- · 2019-02-04 00:32

I encountered the same problem for my application Zap-Guitar (No-Strings-Attached) where I allow users to type/paste/edit their own songs and the app highlights recognized chords.

Yes it is true apple uses an html writer and parser to display the attributed text. A wonderful explanation of behind the scene can be found here: http://www.cocoanetics.com/2012/12/uitextview-caught-with-trousers-down/

The only solution I found for this problem is not to use attributed text which is an overkill for syntax highlighting.

Instead I reverted to the good old UITextView with plain text and added buttons to the text view where highlighted was needed. To compute the buttons frames I used this answer: How to find position or get rect of any word in textview and place buttons over that?

This reduced CPU usage by 30% (give or take).

Here is a handy category:

@implementation UITextView (WithButtons)
- (CGRect)frameForTextRange:(NSRange)range {
    UITextPosition *beginning = self.beginningOfDocument;
    UITextPosition *start = [self positionFromPosition:beginning offset:range.location];
    UITextPosition *end = [self positionFromPosition:start offset:range.length];
    UITextRange *textRange = [self textRangeFromPosition:start toPosition:end];
    CGRect rect = [self firstRectForRange:textRange];
    return [self convertRect:rect fromView:self.textInputView];
}

@end
查看更多
手持菜刀,她持情操
3楼-- · 2019-02-04 00:58

The attributedText accessors have to round-trip to/from HTML, so it's really non-optimal for a syntax-highlighted text view implementation. On iOS 6, you'll probably want to use CoreText directly.

查看更多
登录 后发表回答