Ruben Martinez Jr. Ruben Martinez Jr. - 5 months ago 46
Swift Question

Change UITextView Font w/o Removing Bold/Underline/Italics Formatting

I have a UITextView in an iOS app, and I want to allow the user to bold/italicize/underline/etc at will, so I gave set the allowsEditingTextAttributes property to true on the text view. However, I would also like to allow the user to change things like font and color. Currently, it seems that changing even the color property of the UITextView resets the formatting of the UITextView (i.e. gets rid of the bolding/italics/etc). Is there any way to change this without resetting the formatting? For reference, I am currently doing this to change the color:

self.textView.textColor = colorTheUserPicked //a UIColor picked by the user


EDIT: Actually, this was my bad, I was also resetting the value of the text property itself when I changed the color. Removing that allowed me to change the color as desired. However, I still can't change the font or even the font size without removing the bold/italics/etc. I realize this might be impossible, though...

Answer

I'll answer with an Objective-C solution since I don't code in Swift, but it should be translated easily into Swift.

NSAttributedString "effects" are stored in a NSDictionary. So it's a Key:Value system (with unicity of Key).
Bold/Italic (from your example) are inside the NSFontAttributedName's value. That's why you can't set it again like this.

The main key is to use enumerateAttributesInRange:options:usingBlock::

[attr enumerateAttributesInRange:NSMakeRange(0, [attr length])
                         options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired
                      usingBlock:^(NSDictionary *attributes, NSRange range, BOOL *stop)
 {
     NSMutableDictionary *newAttributes = [attributes mutableCopy];
    //Do changes
    [attr addAttributes:newAttributes range:range]; //Apply effects
 }];

The following lines are at "//Do changes" place. So, if you want to change the Font size:

if ([newAttributes objectForKey:NSFontAttributeName])
{
    UIFont *currentFont = (UIFont *)[attributes objectForKey:NSFontAttributeName];
    UIFont *newFont = [UIFont fontWithName:[currentFont fontName] size:[currentFont pointSize]*0.5];//Here, I multiplied the previous size with 0.5
    [newAttributes setValue:newFont forKey:NSFontAttributeName];
}

If you want to bold/italic, etc. the font you may be interested in UIFontDescriptor, and a related question.

It shows you how to get the previous font and change get a new font with some commons with the previous one.

If you want to change the color:

if ([newAttributes objectForKey:NSForegroundColorAttributeName])//In case if there is some part of the text without color that you don't want to override, or some custom- things
{
     UIColor *newColor = [UIColor blueColor];
    [newAttributes setValue:newColor forKey:NSForegroundColorAttributeName];
}

The underline effect is in NSUnderlineStyleAttributeName's value. So analogic changes can be done.