更改 UITextView 字体 w/o 删除 Bold/Underline/Italics 格式

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

我在 iOS 应用程序中有一个 UITextView,我想允许用户随意 bold/italicize/underline/etc,所以我将文本上的 allowsEditingTextAttributes 属性 设置为 true看法。但是,我还希望允许用户更改字体和颜色等内容。目前,似乎即使更改 UITextView 的颜色 属性 也会重置 UITextView 的格式(即摆脱 bolding/italics/etc)。有没有办法在不重置格式的情况下更改它?作为参考,我目前正在这样做来改变颜色:

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

编辑:实际上,这是我的错,我在更改颜色时还重置了文本 属性 本身的值。删除它可以让我根据需要更改颜色。但是,如果不删除 bold/italics/etc,我仍然无法更改字体甚至字体大小。我意识到这可能是不可能的,但是...

我会用 Objective-C 解决方案来回答,因为我没有在 Swift 中编码,但它应该很容易翻译成 Swift。

NSAttributedString"effects"都存储在一个NSDictionary中。所以这是一个 Key:Value 系统(Key 的唯一性)。
Bold/Italic(来自您的示例)在 NSFontAttributedName 的值内。所以不能再这样设置了。

关键是使用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
 }];

以下行位于“//Do changes”处。 所以,如果你想改变字体大小:

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];
}

如果你想要bold/italic等你可能感兴趣的字体UIFontDescriptor, and a related question.

它向您展示了如何获取以前的字体并更改为与以前的字体有一些共同点的新字体。

如果你想改变颜色:

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];
}

下划线效果在NSUnderlineStyleAttributeName的数值中。所以可以进行类比修改。

谢谢@Larme!您的解决方案是正确的,所以我接受了!这里是 Swift 版本供参考:

//needs to be a mutable attributed string
var mutableCopy = NSMutableAttributedString(attributedString: textView.attributedText)

//make range
var textRange = NSMakeRange(0, textView.attributedText.length)

//get all the string attributes
textView.attributedText.enumerateAttributesInRange(textRange, options: NSAttributedStringEnumerationOptions.LongestEffectiveRangeNotRequired, usingBlock: { (attributes, range, stop) -> Void in
    //make a copy of the attributes we can edit
    var newAttributes = attributes as [NSObject : AnyObject]
    if newAttributes[NSFontAttributeName] != nil { //if the font attr exists
        //create a new font with the old font name and new size
        let currentFont = newAttributes[NSFontAttributeName] as UIFont
        let newFont = UIFont(name: currentFont.fontName, size: currentFont.pointSize * 0.5) //set new font size to half of old size
        //replace the nsfontattribute's font with the new one
        newAttributes[NSFontAttributeName] = newFont
    }
    //replace the old attributes with the new attributes to the mutable version
    mutableCopy.addAttributes(newAttributes, range: range)
})
//replace the old attributed text with the newly attributed text
textView.attributedText = mutableCopy

为 Xamarin.IOS.

的 C# 实现了类似的功能
    NSError error = null;
        var htmlString = new NSAttributedString(NSUrl.FromFilename(
            "About.html"),
            new NSAttributedStringDocumentAttributes {DocumentType = NSDocumentType.HTML},
            ref error);

        var mutableCopy = new NSMutableAttributedString(htmlString);

        htmlString.EnumerateAttributes(new NSRange(0, htmlString.Length),
            NSAttributedStringEnumeration.LongestEffectiveRangeNotRequired,
            (NSDictionary attributes, NSRange range, ref bool stop) =>
            {
                var newMutableAttributes = new NSMutableDictionary(attributes);
                if (newMutableAttributes[UIStringAttributeKey.Font] != null)
                {
                    var currentFont = newMutableAttributes[UIStringAttributeKey.Font] as UIFont;
                    if (currentFont != null)
                    {
                            var newFontName = currentFont.Name;

                            if (currentFont.Name.Equals("TimesNewRomanPS-BoldItalicMT"))
                            {
                                newFontName = "HelveticaNeue-BoldItalic";
                            }
                            else if (currentFont.Name.Equals("TimesNewRomanPS-ItalicMT"))
                            {
                                newFontName = "HelveticaNeue-Italic";
                            }
                            else if (currentFont.Name.Equals("TimesNewRomanPS-BoldMT"))
                            {
                                newFontName = "HelveticaNeue-Bold";
                            }
                            else if (currentFont.Name.Equals("TimesNewRomanPSMT"))
                            {
                                newFontName = "HelveticaNeue";
                            }

                            newMutableAttributes.SetValueForKey(UIFont.FromName(newFontName, currentFont.PointSize * 1.5f), UIStringAttributeKey.Font); //  = newFont;
                    }
                    else
                    {
                        newMutableAttributes.SetValueForKey(UIFont.FromName("HelveticaNeue", 14), UIStringAttributeKey.Font); //  Default to something.
                    }
                }

                mutableCopy.AddAttributes(newMutableAttributes, range);
            });

        aboutTextView.AttributedText = mutableCopy;