在iOS中将HTML转换为NSAttributedString。

时间:2022-10-30 11:47:46

I am using a instance of UIWebView to process some text and color it correctly, it gives the result as HTML but rather than displaying it in the UIWebView I want to display it using Core Text with a NSAttributedString.

我正在使用UIWebView的一个实例来处理一些文本并正确地给它上色,它给出的结果是HTML但不是显示在UIWebView中我想用NSAttributedString的核心文本来显示它。

I am able to create and draw the NSAttributedString but I am unsure how I can convert and map the HTML into the attributed string.

我可以创建并绘制NSAttributedString,但我不确定如何将HTML转换并映射到属性字符串。

I understand that under Mac OS X NSAttributedString has a initWithHTML: method but this was a Mac only addition and is not available for iOS.

我知道在Mac OS X NSAttributedString有initWithHTML:方法,但这是Mac唯一的添加,iOS无法使用。

I also know that there is a similar question to this but it had no answers, I though I would try again and see whether anyone has created a way to do this and if so, if they could share it.

我也知道有一个类似的问题,但它没有答案,我想我会再试一次,看看是否有人发明了一种方法来做这件事,如果有的话,他们是否可以分享。

13 个解决方案

#1


250  

In iOS 7, UIKit added an initWithData:options:documentAttributes:error: method which can initialize an NSAtttributedString using HTML, eg:

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

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

#2


42  

There is a work-in-progress open source addition to NSAttributedString by Oliver Drobnik at Github. It uses NSScanner for HTML parsing.

Github的Oliver Drobnik在NSAttributedString之外,还有一个正在开发的开放源代码。它使用NSScanner进行HTML解析。

#3


20  

Creating an NSAttributedString from HTML must be done on the main thread!

Update: It turns out that NSAttributedString HTML rendering depends on WebKit under the hood, and must be run on the main thread or it will occasionally crash the app with a SIGTRAP.

更新:事实证明NSAttributedString HTML呈现依赖于引擎盖下的WebKit,并且必须在主线程上运行,否则它会偶尔使用一个SIGTRAP来破坏应用程序。

New Relic crash log:

New Relic的崩溃日志:

在iOS中将HTML转换为NSAttributedString。

Below is an updated thread-safe Swift 2 String extension:

以下是更新的线程安全Swift 2字符串扩展:

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

Usage:

用法:

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

Output:

输出:

在iOS中将HTML转换为NSAttributedString。

#4


12  

Swift initializer extension on NSAttributedString

My inclination was to add this as an extension to NSAttributedString rather than String. I tried it as a static extension and an initializer. I prefer the initializer which is what I've included below.

我倾向于把它作为NSAttributedString的扩展,而不是String。我尝试将它作为一个静态扩展和初始化器。我更喜欢初始化器,它是我在下面所包含的。

Swift 4

斯威夫特4

internal convenience init?(html: String) {
    guard let data = html.data(using: String.Encoding.utf16, allowLossyConversion: false) else {
        return nil
    }

    guard let attributedString = try?  NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) else {
        return nil
    }

    self.init(attributedString: attributedString)
}

Swift 3

斯威夫特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)
}
}

Example

例子

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

#5


7  

This is a String extension written in Swift to return a HTML string as NSAttributedString.

这是一个用Swift编写的字符串扩展,以返回一个作为NSAttributedString的HTML字符串。

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

To use,

使用,

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

In the above, I have purposely added a unicode \u2022 to show that it renders unicode correctly.

在上面,我特意添加了一个unicode \u2022来显示它正确地呈现unicode。

A trivial: The default encoding that NSAttributedString uses is NSUTF16StringEncoding (not UTF8!).

一个小问题:NSAttributedString使用的默认编码是NSUTF16StringEncoding(不是UTF8!)

#6


5  

Swift 3.0 Xcode 8 Version

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
}

#7


4  

The only solution you have right now is to parse the HTML, build up some nodes with given point/font/etc attributes, then combine them together into an NSAttributedString. It's a lot of work, but if done correctly, can be reusable in the future.

您现在唯一的解决方案是解析HTML,使用给定的点/字体/等等属性构建一些节点,然后将它们组合成NSAttributedString。这是一项艰巨的工作,但如果做得正确,将来可以重用。

#8


4  

Made some modification on Andrew's solution and update the code to Swift 3:

对Andrew的解决方案做了一些修改,将代码更新为Swift 3:

This code now use UITextView as self and able to inherit its original font, font size and text color

这段代码现在使用UITextView作为self,并且可以继承它的原始字体、字体大小和文本颜色

Note: toHexString() is extension from here

注意: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)
            }
        }
    }
}

Example usage:

使用示例:

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

#9


2  

The above solution is correct.

上面的解决方案是正确的。

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

But the app wioll crash if you are running it on ios 8.1,2 or 3.

但是如果你在ios 8.1 2或3上运行这个应用,它就会崩溃。

To avoid the crash what you can do is : run this in a queue. So that it always be on main thread.

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

#10


2  

Swift 3:
Try this:

斯威夫特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
    }
}  

And for using:

和使用:

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()

#11


2  

Swift 4

斯威夫特4


  • NSAttributedString convenience initializer
  • NSAttributedString方便初始化
  • Without extra guards
  • 没有额外的保安
  • throws error
  • 抛出错误

extension NSAttributedString {

    convenience init(htmlString html: String) throws {
        try self.init(data: Data(html.utf8), options: [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
        ], documentAttributes: nil)
    }

}

Usage

使用

UILabel.attributedText = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!")

#12


1  

Using of NSHTMLTextDocumentType is slow and it is hard to control styles. I suggest you to try my library which is called Atributika. It has its own very fast HTML parser. Also you can have any tag names and define any style for them.

使用NSHTMLTextDocumentType很慢,而且很难控制样式。我建议你试试我的图书馆,叫阿特布提卡。它有自己非常快速的HTML解析器。您还可以拥有任何标记名并为它们定义任何样式。

Example:

例子:

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

label.attributedText = str

You can find it here https://github.com/psharanda/Atributika

您可以在这里找到https://github.com/psharanda/Atributika

#13


0  

Helpful Extensions

Inspired by this thread, a pod, and Erica Sadun's ObjC example in iOS Gourmet Cookbook p.80, I wrote an extension on String and on NSAttributedString to go back and forth between HTML plain-strings and NSAttributedStrings and vice versa -- on GitHub here, which I have found helpful.

灵感来自这个线程,一个pod,和Erica Sadun的ObjC示例在iOS美食烹饪书p。80,我在字符串和NSAttributedString上写了一个扩展,在HTML普通字符串和NSAttributedString之间来回切换,在GitHub上,我找到了帮助。

The signatures are (again, full code in a Gist, link above):

签名(同样是要点中的完整代码,上面的链接):

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 }

#1


250  

In iOS 7, UIKit added an initWithData:options:documentAttributes:error: method which can initialize an NSAtttributedString using HTML, eg:

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

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

#2


42  

There is a work-in-progress open source addition to NSAttributedString by Oliver Drobnik at Github. It uses NSScanner for HTML parsing.

Github的Oliver Drobnik在NSAttributedString之外,还有一个正在开发的开放源代码。它使用NSScanner进行HTML解析。

#3


20  

Creating an NSAttributedString from HTML must be done on the main thread!

Update: It turns out that NSAttributedString HTML rendering depends on WebKit under the hood, and must be run on the main thread or it will occasionally crash the app with a SIGTRAP.

更新:事实证明NSAttributedString HTML呈现依赖于引擎盖下的WebKit,并且必须在主线程上运行,否则它会偶尔使用一个SIGTRAP来破坏应用程序。

New Relic crash log:

New Relic的崩溃日志:

在iOS中将HTML转换为NSAttributedString。

Below is an updated thread-safe Swift 2 String extension:

以下是更新的线程安全Swift 2字符串扩展:

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

Usage:

用法:

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

Output:

输出:

在iOS中将HTML转换为NSAttributedString。

#4


12  

Swift initializer extension on NSAttributedString

My inclination was to add this as an extension to NSAttributedString rather than String. I tried it as a static extension and an initializer. I prefer the initializer which is what I've included below.

我倾向于把它作为NSAttributedString的扩展,而不是String。我尝试将它作为一个静态扩展和初始化器。我更喜欢初始化器,它是我在下面所包含的。

Swift 4

斯威夫特4

internal convenience init?(html: String) {
    guard let data = html.data(using: String.Encoding.utf16, allowLossyConversion: false) else {
        return nil
    }

    guard let attributedString = try?  NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) else {
        return nil
    }

    self.init(attributedString: attributedString)
}

Swift 3

斯威夫特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)
}
}

Example

例子

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

#5


7  

This is a String extension written in Swift to return a HTML string as NSAttributedString.

这是一个用Swift编写的字符串扩展,以返回一个作为NSAttributedString的HTML字符串。

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

To use,

使用,

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

In the above, I have purposely added a unicode \u2022 to show that it renders unicode correctly.

在上面,我特意添加了一个unicode \u2022来显示它正确地呈现unicode。

A trivial: The default encoding that NSAttributedString uses is NSUTF16StringEncoding (not UTF8!).

一个小问题:NSAttributedString使用的默认编码是NSUTF16StringEncoding(不是UTF8!)

#6


5  

Swift 3.0 Xcode 8 Version

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
}

#7


4  

The only solution you have right now is to parse the HTML, build up some nodes with given point/font/etc attributes, then combine them together into an NSAttributedString. It's a lot of work, but if done correctly, can be reusable in the future.

您现在唯一的解决方案是解析HTML,使用给定的点/字体/等等属性构建一些节点,然后将它们组合成NSAttributedString。这是一项艰巨的工作,但如果做得正确,将来可以重用。

#8


4  

Made some modification on Andrew's solution and update the code to Swift 3:

对Andrew的解决方案做了一些修改,将代码更新为Swift 3:

This code now use UITextView as self and able to inherit its original font, font size and text color

这段代码现在使用UITextView作为self,并且可以继承它的原始字体、字体大小和文本颜色

Note: toHexString() is extension from here

注意: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)
            }
        }
    }
}

Example usage:

使用示例:

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

#9


2  

The above solution is correct.

上面的解决方案是正确的。

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

But the app wioll crash if you are running it on ios 8.1,2 or 3.

但是如果你在ios 8.1 2或3上运行这个应用,它就会崩溃。

To avoid the crash what you can do is : run this in a queue. So that it always be on main thread.

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

#10


2  

Swift 3:
Try this:

斯威夫特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
    }
}  

And for using:

和使用:

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()

#11


2  

Swift 4

斯威夫特4


  • NSAttributedString convenience initializer
  • NSAttributedString方便初始化
  • Without extra guards
  • 没有额外的保安
  • throws error
  • 抛出错误

extension NSAttributedString {

    convenience init(htmlString html: String) throws {
        try self.init(data: Data(html.utf8), options: [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
        ], documentAttributes: nil)
    }

}

Usage

使用

UILabel.attributedText = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!")

#12


1  

Using of NSHTMLTextDocumentType is slow and it is hard to control styles. I suggest you to try my library which is called Atributika. It has its own very fast HTML parser. Also you can have any tag names and define any style for them.

使用NSHTMLTextDocumentType很慢,而且很难控制样式。我建议你试试我的图书馆,叫阿特布提卡。它有自己非常快速的HTML解析器。您还可以拥有任何标记名并为它们定义任何样式。

Example:

例子:

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

label.attributedText = str

You can find it here https://github.com/psharanda/Atributika

您可以在这里找到https://github.com/psharanda/Atributika

#13


0  

Helpful Extensions

Inspired by this thread, a pod, and Erica Sadun's ObjC example in iOS Gourmet Cookbook p.80, I wrote an extension on String and on NSAttributedString to go back and forth between HTML plain-strings and NSAttributedStrings and vice versa -- on GitHub here, which I have found helpful.

灵感来自这个线程,一个pod,和Erica Sadun的ObjC示例在iOS美食烹饪书p。80,我在字符串和NSAttributedString上写了一个扩展,在HTML普通字符串和NSAttributedString之间来回切换,在GitHub上,我找到了帮助。

The signatures are (again, full code in a Gist, link above):

签名(同样是要点中的完整代码,上面的链接):

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 }