说我有一个NSMutableAttributedString
。
该字符串具有各种格式:
这是一个例子:
这个字符串在iOS中很难改变,真的很糟糕。
但是,字体本身不是您想要的字体。
我想要:
对于每个角色,将该角色更改为特定字体(例如,Avenir)
但,
对于每个角色,保留以前在该角色上使用的其他属性(粗体,斜体,颜色等)。
你到底怎么这样做?
注意:
如果您在整个范围内轻松添加属性“Avenir”:它只是删除所有其他属性范围,您将丢失所有格式。不幸的是,属性实际上并不是“附加的”。
由于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)
的问题在于它删除了像italic
,bold
或compressed
这样的符号特征,因为它会因某种原因覆盖具有该字体的默认特征的那些特征。为什么这一点完全无法实现,甚至可能是苹果公司的疏忽;或者它“不是一个错误,而是一个功能”,因为我们免费获得新字体的特征。
所以我们要做的是创建一个字体描述符,它具有原始字体的字体描述符中的符号特征:.withSymbolicTraits(f.fontDescriptor.symbolicTraits)
。转发到rmaddy我的迭代的初始代码。
我已经在生产应用程序中发送了这个,我们通过NSAttributedString.DocumentType.html
解析HTML字符串,然后通过上面的扩展名更改字体和颜色。到目前为止没问题。
这是一个更简单的实现,它保留所有属性,包括所有字体属性,除了它允许您更改字体外观。
请注意,这仅使用传入字体的字体(名称)。大小与现有字体保持一致。如果您还想将所有现有字体大小更改为新大小,请将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))
重要 -
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
的注释....你将得到很好的属性范围(斜体,粗体等)但字体将是你不想要的字体。
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
}
}
.
即使这部分工作不重要,也需要一些时间来处理。在实践中,您必须对其进行背景以避免闪烁。
Obmal-C版@manmal的答案
@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
在swift中使用以下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)
挑战:改变/设置属性非常简单,而不反映其他属性。问题是用户想要编辑段落Style属性[if Array value property]而不更改其他段落属性值。
让UITextField完成工作是否有效?
像这样,鉴于attributedString
和newfont
:
let textField = UITextField()
textField.attributedText = attributedString
textField.font = newFont
let resultAttributedString = textField.attributedText
对不起,我错了,它保留了像NSForegroundColorAttributeName这样的“Character Attributes”,例如颜色,但不是UIFontDescriptorSymbolicTraits,它描述粗体,斜体,浓缩等。
那些属于字体而不是“字符属性”。因此,如果您更改字体,您也会更改特征。对不起,但我建议的解决方案不起作用。目标字体需要具有所有可用的特征作为原始字体才能工作。