11
votes

I'm trying to allow different styles of text to be typed into a UITextView, a bit like a text editor using simple attributes such as bold or italics. I understand by using the textView's attributedText property I can apply attributes to certain ranges of text. This is nice, but I would like to be able to type attributed text into the textView, which would be toggled by a button (such as the typing of bold text).

Here's what I've thought of so far:

I've used the -(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text UITextView delegate method to take the text argument, and modify it with attributes by creating an NSAttributedString with the same text. Then create a NSMutableAttributedString, which is a copy of the textView's attributedText. Append the two using appendAttributedString, and then set the textView's attributedText property to the resulting attributedString.

Here's the code:

-(BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text {

   if (self.boldPressed) {

        UIFont *boldFont = [UIFont boldSystemFontOfSize:self.textView.font.pointSize];
        NSDictionary *boldAttr = [NSDictionary dictionaryWithObject:boldFont forKey:NSFontAttributeName];
        NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc]initWithString:text attributes:boldAttr];
        NSMutableAttributedString *textViewText = [[NSMutableAttributedString alloc]initWithAttributedString:textView.attributedText];
        [textViewText appendAttributedString:attributedText];
        textView.attributedText = textViewText;

           return NO;
       }
   return YES;                
}

Having to reset the textViews attributedText every time a character is typed seems like a bit much for a simple action. Not only that, but it doesn't work properly. Here's what it looks like when the bold attribute is enabled:

attributedText issues

There are two problems with this. The most obvious being how every new character is put onto a new line. But what's also strange is that the insertion point is always at the very first index of the text in the textview (only when bold is enabled, yet the bold character is inserted on a new line). So if you're typing with bold enabled, and then turn bold off, typing resumes in front of all existing text.

I'm not sure why these error are happening. I also just don't think that my solution is very efficient, but I can't think of any other way of implementing it.

2
This should work and I think its as good a technique as you will find. Can you add some log messages? Also, why not always write the attributed string - never return YES. You could have a shadow attributed string that you maintain, and always set it in that delegate method instead of intermixing regular and attributed strings. If that fails, upload a demo app with this just this coded and someone (like me) will take a look at it. I'm like 99% sure you can get this to work (but have not done this exactly myself).David H
Turns out the typingAttributes property had to be used, as you can see in the other answer. Adding some log messages ended up making more questions than answers, as all NSAttributedStrings appended to the textView's attributedText would always go to a new line.. Which leads me to conclude that it's just not possible to do in the shouldChangeTextInRange method (hence the setTypingAttributes property). Still not sure why, but setting the typingAttributes seems like a much more elegant solution either way.Joe
Hah - this is why Rob Napier has 70K points! Glad you found a solution - I was sure there was one!David H

2 Answers

38
votes

This is what setTypingAttributes: is for. Set this to your attribute dictionary whenever the user presses one of your attribute buttons and new characters will pick up the requested attributes.

6
votes

Here are sample codes to do so if you are looking for examples

Swift 3.0

var attributes = textField.typingAttributes
attributes["\(NSForegroundColorAttributeName)"] = UIColor.red
textField.typingAttributes = attributes

Objective-C

NSMutableDictionary* attributes = [textField.typingAttributes mutableCopy];
attributes[NSForegroundColorAttributeName] = [UIColor redColor];
textField.typingAttributes = attributes;