Since rmaddy's answer did not work for me (f.fontDescriptor.withFace(font.fontName)
does not keep traits like bold), here is an updated Swift 4 version that also includes color updating:
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()
}
}
Notes
The problem with f.fontDescriptor.withFace(font.fontName)
is that it removes symbolic traits like italic
, bold
or compressed
, since it will for some reason override those with default traits of that font face. Why this is so totally eludes me, it might even be an oversight on Apple's part; or it's "not a bug, but a feature", because we get the new font's traits for free.
So what we have to do is create a font descriptor that has the symbolic traits from the original font's font descriptor: .withSymbolicTraits(f.fontDescriptor.symbolicTraits)
. Props to rmaddy for the initial code on which I iterated.
I've already shipped this in a production app where we parse a HTML string via NSAttributedString.DocumentType.html
and then change the font and color via the extension above. No problems so far.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…