NSAttributedString,改变字体整体但保留所有其他属性?

时间:2017-05-01 17:19:49

标签: swift nsattributedstring

说我有NSMutableAttributedString

该字符串在整个格式中具有各种格式

以下是一个例子:

  

此字符串地狱更改 在iOS 中,真的 糟透了

但是,字体本身并不是您想要的字体。

我想:

对于每个角色

,将该角色更改为特定的字体(例如,Avenir)

但是,

每个字符

保留以前在该字符上的每个其他属性(粗体,斜体,颜色等)。< /强>

你到底怎么做?

注意:

如果你轻易添加属性&#34; Avenir&#34;在整个范围内:只需删除所有其他属性范围,就会丢失所有格式。不幸的是,属性不是,事实上&#34;添加&#34;。

7 个答案:

答案 0 :(得分:38)

由于rmaddy的回答对我不起作用(f.fontDescriptor.withFace(font.fontName)不保留大胆的特征),这里有一个更新的Swift 4版本,其中还包括颜色更新:

extension NSMutableAttributedString {
    func setFontFace(font: UIFont, color: UIColor? = nil) {
        beginEditing()
        self.enumerateAttribute(.font, in: NSRange(location: 0, length: self.length)) { (value, range, stop) in
            if let f = value as? UIFont, let newFontDescriptor = f.fontDescriptor.withFamily(font.familyName).withSymbolicTraits(f.fontDescriptor.symbolicTraits) {
                let newFont = UIFont(descriptor: newFontDescriptor, size: font.pointSize)
                removeAttribute(.font, range: range)
                addAttribute(.font, value: newFont, range: range)
                if let color = color {
                    removeAttribute(.foregroundColor, range: range)
                    addAttribute(.foregroundColor, value: color, range: range)
                }
            }
        }
        endEditing()
    }
}

备注

f.fontDescriptor.withFace(font.fontName)的问题在于它删除了italicboldcompressed等符号特征,因为它会因某种原因覆盖具有该字体默认特征的特征面对。为什么这一点完全无法实现,甚至可能是苹果公司的疏忽;或者它“不是一个错误,而是一个功能”,因为我们免费获得新字体的特征。

所以我们要做的是创建一个字体描述符,它具有原始字体的字体描述符中的符号特征:.withSymbolicTraits(f.fontDescriptor.symbolicTraits)。转向rmaddy以获取我迭代的初始代码。

我已经在生产应用程序中发送了这个,我们通过NSAttributedString.DocumentType.html解析HTML字符串,然后通过上面的扩展名更改字体和颜色。到目前为止没问题。

答案 1 :(得分:6)

这是一个更简单的实现,可以保留所有属性,包括除了它之外的所有字体属性,允许您更改字体。

请注意,这仅使用传入字体的字体(名称)。大小与现有字体保持一致。如果您还要将所有现有字体大小更改为新大小,请将f.pointSize更改为font.pointSize

extension NSMutableAttributedString {
    func replaceFont(with font: UIFont) {
        beginEditing()
        self.enumerateAttribute(.font, in: NSRange(location: 0, length: self.length)) { (value, range, stop) in
            if let f = value as? UIFont {
                let ufd = f.fontDescriptor.withFamily(font.familyName).withSymbolicTraits(f.fontDescriptor.symbolicTraits)!
                let newFont = UIFont(descriptor: ufd, size: f.pointSize)
                removeAttribute(.font, range: range)
                addAttribute(.font, value: newFont, range: range)
            }
        }
        endEditing()
    }
}

使用它:

let someMutableAttributedString = ... // some attributed string with some font face you want to change
someMutableAttributedString.replaceFont(with: UIFont.systemFont(ofSize: 12))

答案 2 :(得分:2)

重要 -

rmaddy为iOS中这个令人讨厌的问题发明了一种全新的技术。

manmal的答案是最终完善的版本。

纯粹的历史记录这里大概是你过去如何去做...

// carefully convert to "our" font - "re-doing" any other formatting.
// change each section BY HAND.  total PITA.

func fixFontsInAttributedStringForUseInApp() {

    cachedAttributedString?.beginEditing()

    let rangeAll = NSRange(location: 0, length: cachedAttributedString!.length)

    var boldRanges: [NSRange] = []
    var italicRanges: [NSRange] = []

    var boldANDItalicRanges: [NSRange] = [] // WTF right ?!

    cachedAttributedString?.enumerateAttribute(
            NSFontAttributeName,
            in: rangeAll,
            options: .longestEffectiveRangeNotRequired)
                { value, range, stop in

                if let font = value as? UIFont {

                    let bb: Bool = font.fontDescriptor.symbolicTraits.contains(.traitBold)
                    let ii: Bool = font.fontDescriptor.symbolicTraits.contains(.traitItalic)

                    // you have to carefully handle the "both" case.........

                    if bb && ii {

                        boldANDItalicRanges.append(range)
                    }

                    if bb && !ii {

                        boldRanges.append(range)
                    }

                    if ii && !bb {

                        italicRanges.append(range)
                    }
                }
            }

    cachedAttributedString!.setAttributes([NSFontAttributeName: font_f], range: rangeAll)

    for r in boldANDItalicRanges {
        cachedAttributedString!.addAttribute(NSFontAttributeName, value: font_fBOTH, range: r)
    }

    for r in boldRanges {
        cachedAttributedString!.addAttribute(NSFontAttributeName, value: font_fb, range: r)
    }

    for r in italicRanges {
        cachedAttributedString!.addAttribute(NSFontAttributeName, value: font_fi, range: r)
    }

    cachedAttributedString?.endEditing()
}

脚注。只是为了清楚相关点。这种事情不可避免地以HTML字符串开头。这里有一个关于如何将一个字符串html转换为NSattributedString的注释....你将得到很好的属性范围(斜体,粗体等)但字体将是你不想要的字体。< / em>的

fileprivate extension String {
    func htmlAttributedString() -> NSAttributedString? {
        guard let data = self.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }
        guard let html = try? NSMutableAttributedString(
            data: data,
            options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
            documentAttributes: nil) else { return nil }
        return html
    }
}  

即使这部分工作不重要,也需要一些时间来处理。在实践中,您必须背景以避免闪烁。

答案 3 :(得分:1)

我为OSX / AppKit支付的2美分(更改

extension NSAttributedString {
// replacing font to all:
func setFont(_ font: NSFont, range: NSRange? = nil)-> NSAttributedString {
    let mas = NSMutableAttributedString(attributedString: self)
    let range = range ?? NSMakeRange(0, self.length)
    mas.addAttributes([.font: font], range: range)
    return NSAttributedString(attributedString: mas)
}


// keeping foot, but changing size:
func setFont(size: CGFloat, range: NSRange? = nil)-> NSAttributedString {
    let mas = NSMutableAttributedString(attributedString: self)
    let range = range ?? NSMakeRange(0, self.length)


    mas.enumerateAttribute(.font, in: range) { value, range, stop in
        if let font = value as? NSFont {
            let name = font.fontName
            let newFont = NSFont(name: name, size: size)
            mas.addAttributes([.font: newFont!], range: range)
        }
    }
    return NSAttributedString(attributedString: mas)

}

答案 4 :(得分:0)

@manmal答案的Obj-C版本

@implementation NSMutableAttributedString (Additions)

- (void)setFontFaceWithFont:(UIFont *)font color:(UIColor *)color {
    [self beginEditing];
    [self enumerateAttribute:NSFontAttributeName
                     inRange:NSMakeRange(0, self.length)
                     options:0
                  usingBlock:^(id  _Nullable value, NSRange range, BOOL * _Nonnull stop) {
                      UIFont *oldFont = (UIFont *)value;
                      UIFontDescriptor *newFontDescriptor = [[oldFont.fontDescriptor fontDescriptorWithFamily:font.familyName] fontDescriptorWithSymbolicTraits:oldFont.fontDescriptor.symbolicTraits];
                      UIFont *newFont = [UIFont fontWithDescriptor:newFontDescriptor size:font.pointSize];
                      if (newFont) {
                          [self removeAttribute:NSFontAttributeName range:range];
                          [self addAttribute:NSFontAttributeName value:newFont range:range];
                      }

                      if (color) {
                          [self removeAttribute:NSForegroundColorAttributeName range:range];
                          [self addAttribute:NSForegroundColorAttributeName value:newFont range:range];
                      }
                  }];
    [self endEditing];
}

@end

答案 5 :(得分:0)

迅速使用以下NSMutableAttributedString扩展名更改具有现有属性的字体。这非常简单,因为我们只需要设置 NSAttributedString.Key.font 属性即可。

extension NSMutableAttributedString {
    @discardableResult
    func setFont(_ font: UIFont, range: NSRange? = nil)-> NSMutableAttributedString {
        let range = range ?? NSMakeRange(0, self.length)
        self.addAttributes([.font: font], range: range)
        return self
    }
}

用法:

let attrString = NSMutableAttributedString("Hello...")
attrString.setFont(UIFont.systemFont(ofSize: 20)

挑战: :更改/设置一个属性而不反映其他属性非常简单。问题是用户希望在不更改其他段落属性值的情况下编辑段落样式属性[如果是数组值属性]。

答案 6 :(得分:-2)

让UITextField完成工作是否有效?

像这样,给定attributedStringnewfont

let textField = UITextField()
textField.attributedText = attributedString
textField.font = newFont
let resultAttributedString = textField.attributedText

抱歉,我错了,它保留了像NSForegroundColorAttributeName这样的“角色属性”,例如颜色,但不是UIFontDescriptorSymbolicTraits,它描述粗体,斜体,浓缩等。

那些属于字体而不是“字符属性”。因此,如果您更改字体,您也会更改特征。对不起,但我建议的解决方案不起作用。目标字体需要具有可用作原始字体的所有特征。