将 HTML 解析为 NSAttributedText-如何设置字体?

我试图获得一个 HTML 格式的文本片段,以便在 UITableViewCell 中的 iPhone 上很好地显示。

到目前为止,我有这个:

NSError* error;
NSString* source = @"<strong>Nice</strong> try, Phil";
NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithData:[source dataUsingEncoding:NSUTF8StringEncoding]
options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]}
documentAttributes:nil error:&error];

这种工作。我得到一些文字,有’尼斯’在粗体!但是... 它也将字体设置为 Times Roman!这不是我想要的字体。 我认为我需要在 document entAttritribute 中设置一些内容,但是,我在任何地方都找不到任何示例。

90772 次浏览

想明白了,有点像熊,也许不是最好的答案。

此代码将遍历所有字体更改。我知道它使用的字体是“ Times New Roman”和“ Times New Roman BoldMT”。 但无论如何,这将找到粗体字体,并让我重置它们。我还可以重设尺寸。

我真诚地希望/认为有一种方法可以在分析时设置它,但是如果有的话我就找不到了。

    NSRange range = (NSRange){0,[str length]};
[str enumerateAttribute:NSFontAttributeName inRange:range options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id value, NSRange range, BOOL *stop) {
UIFont* currentFont = value;
UIFont *replacementFont = nil;


if ([currentFont.fontName rangeOfString:@"bold" options:NSCaseInsensitiveSearch].location != NSNotFound) {
replacementFont = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:25.0f];
} else {
replacementFont = [UIFont fontWithName:@"HelveticaNeue-Thin" size:25.0f];
}


[str addAttribute:NSFontAttributeName value:replacementFont range:range];
}];

是的,有一个更简单的解决方案。在 html 源代码中设置字体!

NSError* error;
NSString* source = @"<strong>Nice</strong> try, Phil";
source = [source stringByAppendingString:@"<style>strong{font-family: 'Avenir-Roman';font-size: 14px;}</style>"];
NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithData:[source dataUsingEncoding:NSUTF8StringEncoding]
options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]}
documentAttributes:nil error:&error];

希望这个能帮上忙。

实际上,还有一种更简单、更干净的方法,那就是在解析 HTML 之后设置字体:

 NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding]
options:@{
NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}
documentAttributes:nil error:nil];
[text addAttributes:@{NSFontAttributeName: [UIFont fontWithName:@"Lato-Regular" size:20]} range:NSMakeRange(0, text.length)];

我实际上找到了一个解决这个问题的有效方法:

在解析之前更改 HTML 响应字符串中的字体。

NSString *aux = [NSString stringWithFormat:@"<span style=\"font-family: YOUR_FONT_NAME; font-size: SIZE\">%@</span>", htmlResponse];

例如:

NSString *aux = [NSString stringWithFormat:@"<span style=\"font-family: HelveticaNeue-Thin; font-size: 17\">%@</span>", [response objectForKey:@"content"]];

快速版:

let aux = "<span style=\"font-family: YOUR_FONT_NAME; font-size: SIZE\">\(htmlResponse)</span>"
#import "UILabel+HTML.h"


@implementation UILabel (HTML)


- (void)jaq_setHTMLFromString:(NSString *)string {


string = [string stringByAppendingString:[NSString stringWithFormat:@"<style>body{font-family: '%@'; font-size:%fpx;}</style>",
self.font.fontName,
self.font.pointSize]];
self.attributedText = [[NSAttributedString alloc] initWithData:[string dataUsingEncoding:NSUnicodeStringEncoding]
options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}
documentAttributes:nil
error:nil];
}




@end

这样你就不需要指定你想要的字体,它会采用标签字体和大小。

Swift 2 版本,基于 Javier Querol 给出的答案

extension UILabel {
func setHTMLFromString(text: String) {
let modifiedFont = NSString(format:"<span style=\"font-family: \(self.font!.fontName); font-size: \(self.font!.pointSize)\">%@</span>", text) as String


let attrStr = try! NSAttributedString(
data: modifiedFont.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)!,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding],
documentAttributes: nil)


self.attributedText = attrStr
}
}

Swift 3.0和 iOS 9 +

extension UILabel {
func setHTMLFromString(htmlText: String) {
let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%@</span>", htmlText)


let attrStr = try! NSAttributedString(
data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue],
documentAttributes: nil)


self.attributedText = attrStr
}
}

Swift 5和 iOS 11 +

extension UILabel {
func setHTMLFromString(htmlText: String) {
let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%@</span>", htmlText)


let attrStr = try! NSAttributedString(
data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue],
documentAttributes: nil)


self.attributedText = attrStr
}
}

如果您在创建 NSAttributedString的同时进行转换,那么以上所有的答案都可以正常工作。但是我认为一个更好的解决方案是下面的类别,这个方案对字符串本身有效,因此不需要访问输入:

extension NSMutableAttributedString
{
func convertFontTo(font: UIFont)
{
var range = NSMakeRange(0, 0)


while (NSMaxRange(range) < length)
{
let attributes = attributesAtIndex(NSMaxRange(range), effectiveRange: &range)
if let oldFont = attributes[NSFontAttributeName]
{
let newFont = UIFont(descriptor: font.fontDescriptor().fontDescriptorWithSymbolicTraits(oldFont.fontDescriptor().symbolicTraits), size: font.pointSize)
addAttribute(NSFontAttributeName, value: newFont, range: range)
}
}
}
}

用途:

let desc = NSMutableAttributedString(attributedString: *someNSAttributedString*)
desc.convertFontTo(UIFont.systemFontOfSize(16))

可以在 iOS7 + 上运行

一种更通用的方法是在枚举时查看字体特征,并创建具有相同特征(粗体、斜体等)的字体:

extension NSMutableAttributedString {


/// Replaces the base font (typically Times) with the given font, while preserving traits like bold and italic
func setBaseFont(baseFont: UIFont, preserveFontSizes: Bool = false) {
let baseDescriptor = baseFont.fontDescriptor
let wholeRange = NSRange(location: 0, length: length)
beginEditing()
enumerateAttribute(.font, in: wholeRange, options: []) { object, range, _ in
guard let font = object as? UIFont else { return }
// Instantiate a font with our base font's family, but with the current range's traits
let traits = font.fontDescriptor.symbolicTraits
guard let descriptor = baseDescriptor.withSymbolicTraits(traits) else { return }
let newSize = preserveFontSizes ? descriptor.pointSize : baseDescriptor.pointSize
let newFont = UIFont(descriptor: descriptor, size: newSize)
self.removeAttribute(.font, range: range)
self.addAttribute(.font, value: newFont, range: range)
}
endEditing()
}
}

改进了 Victor 的解决方案,包括颜色:

extension UILabel {
func setHTMLFromString(text: String) {
let modifiedFont = NSString(format:"<span style=\"color:\(self.textColor.toHexString());font-family: \(self.font!.fontName); font-size: \(self.font!.pointSize)\">%@</span>", text) as String


let attrStr = try! NSAttributedString(
data: modifiedFont.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)!,
options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding],
documentAttributes: nil)


self.attributedText = attrStr
}
}

为了实现这个功能,您还需要将 uicolor 转换为十六进制 https://gist.github.com/yannickl/16f0ed38f0698d9a8ae7的 YLColor.swift

Swift 3 包含 nil 字体的字符串扩展名。不带字体的属性取自其他 SO 问题,不记得是哪一个: (

extension String {
var html2AttributedString: NSAttributedString? {
guard let data = data(using: .utf8) else {
return nil
}


do {
return try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)
}
catch {
print(error.localizedDescription)
return nil
}
}


public func getHtml2AttributedString(font: UIFont?) -> NSAttributedString? {
guard let font = font else {
return html2AttributedString
}


let modifiedString = "<style>body{font-family: '\(font.fontName)'; font-size:\(font.pointSize)px;}</style>\(self)";


guard let data = modifiedString.data(using: .utf8) else {
return nil
}


do {
return try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)
}
catch {
print(error)
return nil
}
}
}

使用 NSHTMLTextDocumentType 缓慢且难以控制样式。我建议你试试我的图书馆叫 Atributika。它有自己的非常快的解析器。您还可以拥有任何标记名称,并为它们定义任何样式。

例如:

let str = "<strong>Nice</strong> try, Phil".style(tags:
Style("strong").font(.boldSystemFont(ofSize: 15))).attributedString


label.attributedText = str

你可以在这里找到它

谢谢你的回答,我真的很喜欢扩展,但我还没有转换到迅速。对于那些还在学习 Objective-C 的老学生来说,这应该有所帮助

-(void) setBaseFont:(UIFont*)font preserveSize:(BOOL) bPreserve {


UIFontDescriptor *baseDescriptor = font.fontDescriptor;


[self enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, [self length]) options:0 usingBlock:^(id  _Nullable value, NSRange range, BOOL * _Nonnull stop) {


UIFont *font = (UIFont*)value;
UIFontDescriptorSymbolicTraits traits = font.fontDescriptor.symbolicTraits;
UIFontDescriptor *descriptor = [baseDescriptor fontDescriptorWithSymbolicTraits:traits];
UIFont *newFont = [UIFont fontWithDescriptor:descriptor size:bPreserve?baseDescriptor.pointSize:descriptor.pointSize];


[self removeAttribute:NSFontAttributeName range:range];
[self addAttribute:NSFontAttributeName value:newFont range:range];


}];    }

编码愉快! ——格雷格 · 弗雷姆

Swift 4 + 更新 UILabel扩展

extension UILabel {
func setHTMLFromString(text: String) {
let modifiedFont = NSString(format:"<span style=\"font-family: \(self.font!.fontName); font-size: \(self.font!.pointSize)\">%@</span>" as NSString, text)


let attrStr = try! NSAttributedString(
data: modifiedFont.data(using: String.Encoding.unicode.rawValue, allowLossyConversion: true)!,
options: [NSAttributedString.DocumentReadingOptionKey.documentType:NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue],
documentAttributes: nil)


self.attributedText = attrStr
}
}

IOS9 +

extension UILabel {
func setHTMLFromString(htmlText: String) {
let modifiedFont = NSString(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%@</span>" as NSString, htmlText) as String




//process collection values
let attrStr = try! NSAttributedString(
data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
options: [NSAttributedString.DocumentReadingOptionKey.documentType:NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue],
documentAttributes: nil)




self.attributedText = attrStr
}
}

结合每个人的回答,我做了两个扩展,它们允许用 html 文本设置标签。上面的一些答案没有正确解释属性字符串中的字体族。其他人对于我的需要来说是不完整的,或者在其他方面失败了。有什么需要我改进的地方就告诉我。

我希望这能帮到别人。

extension UILabel {
/// Sets the label using the supplied html, using the label's font and font size as a basis.
/// For predictable results, using only simple html without style sheets.
/// See https://stackoverflow.com/questions/19921972/parsing-html-into-nsattributedtext-how-to-set-font
///
/// - Returns: Whether the text could be converted.
@discardableResult func setAttributedText(fromHtml html: String) -> Bool {
guard let data = html.data(using: .utf8, allowLossyConversion: true) else {
print(">>> Could not create UTF8 formatted data from \(html)")
return false
}


do {
let mutableText = try NSMutableAttributedString(
data: data,
options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue],
documentAttributes: nil)
mutableText.replaceFonts(with: font)
self.attributedText = mutableText
return true
} catch (let error) {
print(">>> Could not create attributed text from \(html)\nError: \(error)")
return false
}
}
}


extension NSMutableAttributedString {


/// Replace any font with the specified font (including its pointSize) while still keeping
/// all other attributes like bold, italics, spacing, etc.
/// See https://stackoverflow.com/questions/19921972/parsing-html-into-nsattributedtext-how-to-set-font
func replaceFonts(with font: UIFont) {
let baseFontDescriptor = font.fontDescriptor
var changes = [NSRange: UIFont]()
enumerateAttribute(.font, in: NSMakeRange(0, length), options: []) { foundFont, range, _ in
if let htmlTraits = (foundFont as? UIFont)?.fontDescriptor.symbolicTraits,
let adjustedDescriptor = baseFontDescriptor.withSymbolicTraits(htmlTraits) {
let newFont = UIFont(descriptor: adjustedDescriptor, size: font.pointSize)
changes[range] = newFont
}
}
changes.forEach { range, newFont in
removeAttribute(.font, range: range)
addAttribute(.font, value: newFont, range: range)
}
}
}

下面是使用 Objective-C 返回 NSAttributedString 的 NSString 扩展。

它可以正确地处理带有 HTML 标记的字符串,并设置所需的字体和字体颜色,同时保留包括 BOLD、 ITALICS 在内的 HTML 标记。

最重要的是,它不依赖于任何 HTML 标记来设置字体属性。

@implementation NSString (AUIViewFactory)


- (NSAttributedString*)attributedStringFromHtmlUsingFont:(UIFont*)font fontColor:(UIColor*)fontColor
{
NSMutableAttributedString* mutableAttributedString = [[[NSAttributedString alloc] initWithData:[self dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute : @(NSUTF8StringEncoding)} documentAttributes:nil error:nil] mutableCopy]; // parse text with html tags into a mutable attributed string
[mutableAttributedString beginEditing];
// html tags cause font ranges to be created, for example "This text is <b>bold</b> now." creates three font ranges: "This text is " , "bold" , " now."
[mutableAttributedString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, mutableAttributedString.length) options:0 usingBlock:^(id value, NSRange range, BOOL* stop)
{ // iterate every font range, change every font to new font but preserve symbolic traits such as bold and italic (underline and strikethorugh are preserved automatically), set font color
if (value)
{
UIFont* oldFont = (UIFont*)value;
UIFontDescriptor* fontDescriptor = [font.fontDescriptor fontDescriptorWithSymbolicTraits:oldFont.fontDescriptor.symbolicTraits];
UIFont* newFont = [UIFont fontWithDescriptor:fontDescriptor size:font.pointSize];
[mutableAttributedString removeAttribute:NSFontAttributeName range:range]; // remove the old font attribute from this range
[mutableAttributedString addAttribute:NSFontAttributeName value:newFont range:range]; // add the new font attribute to this range
[mutableAttributedString addAttribute:NSForegroundColorAttributeName value:fontColor range:range]; // set the font color for this range
}
}];
[mutableAttributedString endEditing];
return mutableAttributedString;
}


@end

Swift 5 UILabel 和 UITextView 解决方案

extension UITextView {
func setHTMLFromString(htmlText: String) {
let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%@</span>", htmlText)


let attrStr = try! NSAttributedString(
data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue],
documentAttributes: nil)


self.attributedText = attrStr
}

}

extension UILabel {
func setHTMLFromString(htmlText: String) {
let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%@</span>", htmlText)


let attrStr = try! NSAttributedString(
data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue],
documentAttributes: nil)


self.attributedText = attrStr
}

}

UILabel 的用法

self.label.setHTMLFromString(htmlText: htmlString)

UITextView 的用法

self.textView.setHTMLFromString(htmlText: htmlString)

输出

enter image description here

  • 迅捷解决方案

以下方法可行。在这种方法中,您可以很好地提供字体系列、字体大小和颜色。请随时提出改变或任何更好的方法来做到这一点。

extension UILabel {


func setHTMLFromString(htmlText: String,fontFamily:String,fontColor:String) {


let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', \(fontFamily); font-size: \(self.font!.pointSize); color: \(fontColor) ; \">%@</span>", htmlText)


do{
if let valData = modifiedFont.data(using: .utf8){
let attrStr = try NSAttributedString(data: valData, options: [NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html.rawValue], documentAttributes: nil)
self.attributedText = attrStr
}
}catch{
print("Conversion failed with \(error)")
self.attributedText = nil
}
}