18

I have a UITextView and when the user is entering text into it, I want to format the text on the fly. Something like syntax highlighting...

For that I'd like to use UITextView...

Everything works fine expect one problem: I take the text from the text view and make an NSAttributedString from of it. I make some edits to this attributed string and set it back as the textView.attributedText.

This happens everytime the user types. So I have to remember the selectedTextRange before the edit to the attributedText and set it back afterwards so that the user can continue typing at the place he was typing before. The only problem is that once the text is long enough to require scrolling, the UITextView will now start scrolling to the top if I type slowly.

Here is some sample code:

- (void)formatTextInTextView:(UITextView *)textView
{
  NSRange selectedRange = textView.selectedRange;
  NSString *text = textView.text;

  // This will give me an attributedString with the base text-style
  NSMutableAttributedString *attributedString = [self attributedStringFromString:text];

  NSError *error = nil;
  NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"#(\\w+)" options:0 error:&error];
  NSArray *matches = [regex matchesInString:text
                                    options:0
                                      range:NSMakeRange(0, text.length)];

  for (NSTextCheckingResult *match in matches)
  {
    NSRange matchRange = [match rangeAtIndex:0];
    [attributedString addAttribute:NSForegroundColorAttributeName
                             value:[UIColor redColor]
                             range:matchRange];
  }

  textView.attributedText = attributedString;
  textView.selectedRange = selectedRange;
}

Is there any solution without using CoreText directly? I like the UITextViews ability to select text and so on....

1
  • 1
    @NANNAV Thanks for your efforts to improve SO by editing posts. However, there's really no reason for the arbitrary bold emphasis you seem to introduce in various posts. It only introduces noise and can actually be considered actively harmful. Commented Jun 4, 2013 at 9:48

4 Answers 4

40
+50

I am not sure that this is correct solution, but it works.
Just disable scrolling before formatting text and enable it after formatting

- (void)formatTextInTextView:(UITextView *)textView
{
    textView.scrollEnabled = NO;
    NSRange selectedRange = textView.selectedRange;
    NSString *text = textView.text;

    // This will give me an attributedString with the base text-style
    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:text];

    NSError *error = nil;
    NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"#(\\w+)" options:0 error:&error];
    NSArray *matches = [regex matchesInString:text
                                      options:0
                                        range:NSMakeRange(0, text.length)];

    for (NSTextCheckingResult *match in matches)
    {
        NSRange matchRange = [match rangeAtIndex:0];
        [attributedString addAttribute:NSForegroundColorAttributeName
                                 value:[UIColor redColor]
                                 range:matchRange];
    }

    textView.attributedText = attributedString;
    textView.selectedRange = selectedRange;
    textView.scrollEnabled = YES;
}
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your answer!!! :) In the meantime I found another solution... I just subclassed UITextView and overwrote the 'scrollRectToVisible:animated:' and let it call super with no animations... Your solution seems a bit smoother :) Again, thanks a lot!
Update for Swift 5+: NSForegroundColorAttributeName should be replaced with NSAttributedString.Key.foregroundColor
5

Used Sergeys's answer myself and ported it to Swift 2:

func formatTextInTextView(textView: UITextView) {
    textView.scrollEnabled = false
    let selectedRange = textView.selectedRange
    let text = textView.text

    // This will give me an attributedString with the base text-style
    let attributedString = NSMutableAttributedString(string: text)

    let regex = try? NSRegularExpression(pattern: "#(\\w+)", options: [])
    let matches = regex!.matchesInString(text, options: [], range: NSMakeRange(0, text.characters.count))

    for match in matches {
        let matchRange = match.rangeAtIndex(0)
        attributedString.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: matchRange)
    }

    textView.attributedText = attributedString
    textView.selectedRange = selectedRange
    textView.scrollEnabled = true
}

1 Comment

When i call this method? Actually i want same like your code but as link. Can you share that code.
2

In Swift 4:

func createAttributedText() {
    let stringText = "Test String"
    let stringCount = stringText.count
    let string: NSMutableAttributedString = NSMutableAttributedString(string: stringText)

    string.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: NSMakeRange(0, stringCount))

    self.textView.attributedText =  string
}

Comments

1

Swift 2.0:

let myDisplayTxt:String = "Test String"

    let string: NSMutableAttributedString = NSMutableAttributedString(string: self.myDisplayTxt)
    string.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSMakeRange(0, 5))
    string.addAttribute(String(kCTForegroundColorAttributeName), value: UIColor.redColor().CGColor as AnyObject, range: NSMakeRange(0, 5))

    self.sampleTextView.attributedText =  string

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.