在iOS中将HTML转换为NSAttributedString

我正在使用UIWebView一个实例来处理一些文本和正确的颜色,它给出的结果作为HTML,而不是显示在UIWebView我想显示它使用Core TextNSAttributedString

我能够创build和绘制NSAttributedString但我不确定如何转换和映射到属性string的HTML。

我知道在Mac OS X下, NSAttributedString有一个initWithHTML:方法,但是这只是一个Mac的附加,不适用于iOS。

我也知道还有一个类似的问题,但是没有答案,我虽然会再试一次,看是不是有人创造了这样做的方法,如果可以的话,是否可以分享。

在iOS 7中,UIKit添加了一个initWithData:options:documentAttributes:error:方法,它可以使用HTML初始化NSAtttributedString,例如:

 [[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)} documentAttributes:nil error:nil]; 

Oliver Drobnik在Github上还有一个工作进行中的开源项目,除了NSAttributedString 。 它使用NSScanner进行HTMLparsing。

从HTML创buildNSAttributedString必须在主线程上完成!

更新:事实certificate,NSAttributedString HTML呈现取决于WebKit的底线, 必须在主线程上运行, 否则偶尔会使应用程序崩溃SIGTRAP

New Relic崩溃日志:

在这里输入图像描述

下面是一个更新的线程安全的 Swift 2string扩展:

 extension String { func attributedStringFromHTML(completionBlock:NSAttributedString? ->()) { guard let data = dataUsingEncoding(NSUTF8StringEncoding) else { print("Unable to decode data from html string: \(self)") return completionBlock(nil) } let options = [NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSNumber(unsignedInteger:NSUTF8StringEncoding)] dispatch_async(dispatch_get_main_queue()) { if let attributedString = try? NSAttributedString(data: data, options: options, documentAttributes: nil) { completionBlock(attributedString) } else { print("Unable to create attributed string from html string: \(self)") completionBlock(nil) } } } } 

用法:

 let html = "<center>Here is some <b>HTML</b></center>" html.attributedStringFromHTML { attString in self.bodyLabel.attributedText = attString } 

输出:

在这里输入图像描述

这是一个在Swift中编写的String扩展,用于将HTMLstring作为NSAttributedString返回。

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

要使用,

 label.attributedText = "<b>Hello</b> \u{2022} babe".htmlAttributedString() 

在上面,我故意添加了一个unicode \ u2022来表明它正确渲染unicode。

一个微不足道的: NSAttributedString使用的默认编码是NSUTF16StringEncoding (而不是UTF8!)。

NSAttributedString上的Swift初始值设定项扩展

我的意思是把这个扩展添加到NSAttributedString而不是String 。 我试过它作为一个静态的扩展和初始化。 我更喜欢下面包含的初始化程序。

斯威夫特4

 extension NSAttributedString { internal convenience init?(html: String) { guard let data = html.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil } guard let attributedString = try? NSMutableAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil) else { return nil } self.init(attributedString: attributedString) } } 

Swift 3

 extension NSAttributedString { internal convenience init?(html: String) { guard let data = html.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil } guard let attributedString = try? NSMutableAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) else { return nil } self.init(attributedString: attributedString) } } 

 let html = "<b>Hello World!</b>" let attributedString = NSAttributedString(html: html) 

Swift 3.0 Xcode 8版本

 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 } 

你现在唯一的解决scheme是parsingHTML,使用给定的point / font / etc属性构build一些节点,然后将它们组合成一个NSAttributedString。 这是很多工作,但是如果做得对,将来可以重复使用。

对Andrew的解决scheme进行了一些修改,并将代码更新到Swift 3:

此代码现在使用UITextView作为self并能够inheritance其原始字体,字体大小和文本颜色

注意: toHexString()是来自这里的扩展

 extension UITextView { func setAttributedStringFromHTML(_ htmlCode: String, completionBlock: @escaping (NSAttributedString?) ->()) { let inputText = "\(htmlCode)<style>body { font-family: '\((self.font?.fontName)!)'; font-size:\((self.font?.pointSize)!)px; color: \((self.textColor)!.toHexString()); }</style>" guard let data = inputText.data(using: String.Encoding.utf16) else { print("Unable to decode data from html string: \(self)") return completionBlock(nil) } DispatchQueue.main.async { if let attributedString = try? NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType], documentAttributes: nil) { self.attributedText = attributedString completionBlock(attributedString) } else { print("Unable to create attributed string from html string: \(self)") completionBlock(nil) } } } } 

用法示例:

 mainTextView.setAttributedStringFromHTML("<i>Hello world!</i>") { _ in } 

以上解决scheme是正确的。

 [[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)} documentAttributes:nil error:nil]; 

但是,如果你在ios 8.1,2或3上运行应用程序wioll崩溃。

为了避免崩溃,你可以做的是:在队列中运行。 所以它总是在主线上。

Swift 3
试试这个

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

并用于:

 let str = "<h1>Hello bro</h1><h2>Come On</h2><h3>Go sis</h3><ul><li>ME 1</li><li>ME 2</li></ul> <p>It is me bro , remember please</p>" self.contentLabel.attributedText = str.htmlAttributedString() 

有用的扩展

在iOS Gourmet Cookbook第80页的这个主题,一个pod和Erica Sadun的ObjC例子的启发下,我写了一个关于StringNSAttributedString的扩展,以便在HTML纯string和NSAttributedStrings之间来回切换,反之亦然 – 在GitHub上,我发现有帮助。

签名 (同上,链接上面的完整代码):

 extension NSAttributedString { func encodedString(ext: DocEXT) -> String? static func fromEncodedString(_ eString: String, ext: DocEXT) -> NSAttributedString? static func fromHTML(_ html: String) -> NSAttributedString? // same as above, where ext = .html } extension String { func attributedString(ext: DocEXT) -> NSAttributedString? } enum DocEXT: String { case rtfd, rtf, htm, html, txt } 

使用NSHTMLTextDocumentType很慢,很难控制样式。 我build议你去尝试一下我的图书馆,叫做Atributika。 它有自己的非常快速的HTMLparsing器。 你也可以有任何标签名称并为其定义任何样式。

例:

 let str = "<strong>Hello</strong> World!".style(tags: Style("strong").font(.boldSystemFont(ofSize: 15))).attributedString label.attributedText = str 

你可以在这里findhttps://github.com/psharanda/Atributika