在iOS中将HTML转换为NSAttributedString

问题描述 投票:139回答:14

我正在使用UIWebView的一个实例处理一些文本并正确地着色它,它将结果显示为HTML,而不是在UIWebView中显示它我想使用Core TextNSAttributedString显示它。

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

据我所知,在Mac OS X下,NSAttributedString有一个initWithHTML:方法,但这只是Mac的补充,不适用于iOS。

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

iphone objective-c cocoa-touch core-text nsattributedstring
14个回答
275
投票

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

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

在Swift中:

let htmlData = NSString(string: details).data(using: String.Encoding.unicode.rawValue)
let options = [NSAttributedString.DocumentReadingOptionKey.documentType:
        NSAttributedString.DocumentType.html]
let attributedString = try? NSMutableAttributedString(data: htmlData ?? Data(),
                                                          options: options,
                                                          documentAttributes: nil)

2
投票

以上解决方案是正确的。

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

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

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


2
投票

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

例:

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

label.attributedText = str

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


2
投票

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

0
投票

Helpful Extensions

受到这个主题,pod和Erica Sadun在iOS Gourmet Cookbook p.80中的ObjC示例的启发,我在StringNSAttributedString上编写了一个扩展,在HTML普通字符串和NSAttributedStrings之间来回反复,反之亦然 - 在GitHub上here ,我发现有帮助。

签名是(再次,Gist中的完整代码,上面的链接):

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 }

0
投票

用字体

extension NSAttributedString
{
internal convenience init?(html: String, font: UIFont? = nil) {
    guard let data = html.data(using: String.Encoding.utf16, allowLossyConversion: false) else {
        return nil
    }
    assert(Thread.isMainThread)
    guard let attributedString = try?  NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) else {
        return nil
    }
    let mutable = NSMutableAttributedString(attributedString: attributedString)
    if let font = font {
        mutable.addAttribute(.font, value: font, range: NSRange(location: 0, length: mutable.length))
    }
    self.init(attributedString: mutable)
}
}

或者您可以使用从派生自的版本并在设置attributedString后在UILabel上设置字体


42
投票

Github的Oliver Drobnik正在进行一项正在进行中的open source addition to NSAttributedString。它使用NSScanner进行HTML解析。


23
投票

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

更新:事实证明,NSAttributedString HTML呈现依赖于WebKit,必须在主线程上运行,否则偶尔会使用SIGTRAP使应用程序崩溃。

新Relic崩溃日志:

enter image description here

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

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
}

输出:

enter image description here


16
投票

Swift initializer extension on NSAttributedString

我倾向于将此作为NSAttributedString而不是String的延伸。我尝试将它作为静态扩展和初始化器。我更喜欢初始化器,这是我在下面包含的内容。

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

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

8
投票

这是一个用Swift编写的String扩展,用于返回一个HTML字符串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!)。


5
投票

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
}

5
投票

斯威夫特4


  • NSAttributedString方便初始化程序
  • 没有额外的警卫
  • 抛出错误

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

}

用法

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

4
投票

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


4
投票

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

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

注意:toHexString()here的延伸

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 }
© www.soinside.com 2019 - 2024. All rights reserved.