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