Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/objective-c/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Iphone 在iOS中将HTML转换为NSAttributedString_Iphone_Objective C_Cocoa Touch_Core Text_Nsattributedstring - Fatal编程技术网

Iphone 在iOS中将HTML转换为NSAttributedString

Iphone 在iOS中将HTML转换为NSAttributedString,iphone,objective-c,cocoa-touch,core-text,nsattributedstring,Iphone,Objective C,Cocoa Touch,Core Text,Nsattributedstring,我正在使用UIWebView的一个实例来处理一些文本并正确地为其着色,它以HTML的形式给出结果,但不是在UIWebView中显示它,而是使用Core text和nsattributed字符串来显示它 我能够创建并绘制NSAttributedString,但我不确定如何将HTML转换并映射到属性字符串 我知道在Mac OS X下,NSAttributedString有一个initWithHTML:方法,但这是一个仅适用于Mac的添加,不适用于iOS 我也知道有一个类似的问题,但没有答案,我想我会

我正在使用
UIWebView
的一个实例来处理一些文本并正确地为其着色,它以HTML的形式给出结果,但不是在
UIWebView
中显示它,而是使用
Core text
nsattributed字符串来显示它

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

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


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

现在唯一的解决方案是解析HTML,建立一些具有给定点/font/etc属性的节点,然后将它们组合成一个NSAttributed字符串。这是一项大量的工作,但如果做得正确,将来可以重复使用。

Github的Oliver Drobnik正在进行一项工作。它使用NSScanner进行HTML解析。

在iOS 7中,UIKit添加了一种方法,可以使用HTML初始化
NSAttributedString
,例如:

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

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)
从HTML创建NSAttributedString必须在主线程上完成! 更新:NSAttributedString HTML呈现取决于引擎盖下的WebKit,必须在主线程上运行,否则偶尔会导致应用程序崩溃

New Relic崩溃日志:

以下是更新的线程安全的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)
            }
        }
    }
}
用法:

let html = "<center>Here is some <b>HTML</b></center>"
html.attributedStringFromHTML { attString in
    self.bodyLabel.attributedText = attString
}
let html=“这是一些html”
html.attributedStringFromHTML{attString in
self.bodyLabel.AttributeText=attString
}
输出:


上述解决方案是正确的

[[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding] 
                                 options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                           NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)} 
                      documentAttributes:nil error:nil];
但如果在ios 8.1、2或3上运行,应用程序wioll将崩溃。


要避免崩溃,您可以做的是:在队列中运行此命令。因此它总是在主线程上。

这是一个用Swift编写的
字符串
扩展,用于返回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()
label.attributeText=“Hello\u{2022}babe”.htmlAttributedString()
在上面,我特意添加了一个unicode\u2022,以显示它正确呈现unicode

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

有用的扩展 受这个线程、一个pod和Erica Sadun在iOS Gourmet Cookbook p.80中的ObjC示例的启发,我在GitHub上编写了一个扩展名
String
NSAttributedString
,在HTML纯字符串和NSAttributedString之间来回切换,反之亦然,我发现这很有帮助

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


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
}
对的解决方案进行了一些修改,并将代码更新为Swift 3:

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

注意:
toHexString()
是的扩展名

扩展UITextView{ func setAttributedStringFromHTML(htmlCode:String,completionBlock:@escaping(NSAttributedString?)->()){ 让inputText=“\(htmlCode)正文{字体系列:'\((self.font?.fontName)!)”;字体大小:\((self.font?.pointSize)!)px;颜色:\((self.textColor)!.tohextString();) guard let data=inputText.data(使用:String.Encoding.utf16)else{ 打印(“无法解码html字符串中的数据:\(self)”) 返回完成块(nil) } DispatchQueue.main.async{ 如果let attributedString=try?NSAttributedString(数据:数据,选项:[NSDocumentTypeDocumentAttribute:NSHTMLTextDocumentType],DocumentAttribute:nil){ self.attributedText=attributedString completionBlock(attributedString) }否则{ 打印(“无法从html字符串创建属性字符串:\(self)”) 完成块(无) } } } }
用法示例:

mainTextView.setAttributedStringFromHTML("<i>Hello world!</i>") { _ in }
mainTextView.setAttributedStringFromHTML(“你好,世界!”){in}

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

例如:

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

label.attributedText = str
let str=“HelloWorld!”。样式(标记:
样式(“强”).font(.boldSystemFont(大小:15)).attributedString
label.attributeText=str

您可以在这里找到它

Swift 3
试试这个:

及使用:

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()
let str=“你好,broCome OnGo sis
  • 我1
  • 我2
  • 是我,兄弟,请记住

    ” self.contentLabel.AttributeText=str.htmlAttributedString()
NSAttribute字符串上的Swift初始值设定项扩展 我倾向于将其作为
NSAttributedString
的扩展,而不是
String
。我尝试将其作为静态扩展和初始值设定项。我更喜欢初始值设定项,这是我在下面包含的


Swift 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

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)
UILabel.attributedText = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!")
让html=“你好,世界!”
让attributedString=NSAttributedString(html:html)

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

  • NSAttributedString便利初始值设定项
  • 没有额外的警卫
  • 抛出错误

用法

let html = "<b>Hello World!</b>"
let attributedString = NSAttributedString(html: html)
UILabel.attributedText = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!")
UILabel.attributedText=try?NSAttributedString(htmlString:你好世界!”)

向字体家族致敬,动态字体我炮制了这个令人憎恶的东西:

extension NSAttributedString
{
    convenience fileprivate init?(html: String, font: UIFont? = Font.dynamic(style: .subheadline))
    {
        guard let data = html.data(using: String.Encoding.utf8, allowLossyConversion: true) else {
        var totalString = html
        /*
         https://stackoverflow.com/questions/32660748/how-to-use-apples-new-san-francisco-font-on-a-webpage
            .AppleSystemUIFont I get in font.familyName does not work
         while -apple-system does:
         */
        var ffamily = "-apple-system"
        if let font = font {
            let lLDBsucks = font.familyName
            if !lLDBsucks.hasPrefix(".appleSystem") {
                ffamily = font.familyName
            }
            totalString = "<style>\nhtml * {font-family: \(ffamily) !important;}\n            </style>\n" + html
        }
        guard let data = totalString.data(using: String.Encoding.utf8, allowLossyConversion: true) else {
            return nil
        }
        assert(Thread.isMainThread)
        guard let attributedText = try?  NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) else {
            return nil
        }
        let mutable = NSMutableAttributedString(attributedString: attributedText)
        if let font = font {
        do {
            var found = false
            mutable.beginEditing()
            mutable.enumerateAttribute(NSAttributedString.Key.font, in: NSMakeRange(0, attributedText.length), options: NSAttributedString.EnumerationOptions(rawValue: 0)) { (value, range, stop) in
                    if let oldFont = value as? UIFont {
                        let newsize = oldFont.pointSize * 15 * Font.scaleHeruistic / 12
                        let newFont = oldFont.withSize(newsize)
                        mutable.addAttribute(NSAttributedString.Key.font, value: newFont, range: range)
                        found = true
                    }
                }
                if !found {
                    // No font was found - do something else?
                }

            mutable.endEditing()
            
//            mutable.addAttribute(.font, value: font, range: NSRange(location: 0, length: mutable.length))
        }
        self.init(attributedString: mutable)
    }

}
扩展NSAttributedStrin
extension NSAttributedString
{
    convenience fileprivate init?(html: String, font: UIFont? = Font.dynamic(style: .subheadline))
    {
        guard let data = html.data(using: String.Encoding.utf8, allowLossyConversion: true) else {
        var totalString = html
        /*
         https://stackoverflow.com/questions/32660748/how-to-use-apples-new-san-francisco-font-on-a-webpage
            .AppleSystemUIFont I get in font.familyName does not work
         while -apple-system does:
         */
        var ffamily = "-apple-system"
        if let font = font {
            let lLDBsucks = font.familyName
            if !lLDBsucks.hasPrefix(".appleSystem") {
                ffamily = font.familyName
            }
            totalString = "<style>\nhtml * {font-family: \(ffamily) !important;}\n            </style>\n" + html
        }
        guard let data = totalString.data(using: String.Encoding.utf8, allowLossyConversion: true) else {
            return nil
        }
        assert(Thread.isMainThread)
        guard let attributedText = try?  NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding: String.Encoding.utf8.rawValue], documentAttributes: nil) else {
            return nil
        }
        let mutable = NSMutableAttributedString(attributedString: attributedText)
        if let font = font {
        do {
            var found = false
            mutable.beginEditing()
            mutable.enumerateAttribute(NSAttributedString.Key.font, in: NSMakeRange(0, attributedText.length), options: NSAttributedString.EnumerationOptions(rawValue: 0)) { (value, range, stop) in
                    if let oldFont = value as? UIFont {
                        let newsize = oldFont.pointSize * 15 * Font.scaleHeruistic / 12
                        let newFont = oldFont.withSize(newsize)
                        mutable.addAttribute(NSAttributedString.Key.font, value: newFont, range: range)
                        found = true
                    }
                }
                if !found {
                    // No font was found - do something else?
                }

            mutable.endEditing()
            
//            mutable.addAttribute(.font, value: font, range: NSRange(location: 0, length: mutable.length))
        }
        self.init(attributedString: mutable)
    }

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

        let options : [DocumentReadingOptionKey : Any] = [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
        ]

        guard
            let string = try? NSMutableAttributedString(data: data, options: options,
                                                 documentAttributes: nil) else { return nil }

        if #available(iOS 13, *) {
            let colour = [NSAttributedString.Key.foregroundColor: UIColor.label]
            string.addAttributes(colour, range: NSRange(location: 0, length: string.length))
        }

        self.init(attributedString: string)
    }
}
public extension NSAttributedString {
    convenience init?(_ html: String) {
        guard let data = html.data(using: .unicode) else {
                return nil
        }

        try? self.init(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil)
    }
}