iphone - Convierte HTML a NSAttributedString en iOS
objective-c cocoa-touch (13)
Extensión de inicializador Swift en NSAttributedString
Mi inclinación era agregar esto como una extensión a NSAttributedString
lugar de String
. Lo intenté como una extensión estática y un inicializador. Prefiero el inicializador, que es lo que he incluido a continuación.
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)
}
}
Ejemplo
let html = "<b>Hello World!</b>"
let attributedString = NSAttributedString(html: html)
Estoy usando una instancia de UIWebView
para procesar texto y colorearlo correctamente, da el resultado como HTML pero en lugar de mostrarlo en UIWebView
, quiero mostrarlo usando Core Text
con NSAttributedString
.
Puedo crear y dibujar NSAttributedString
pero no estoy seguro de cómo puedo convertir y mapear el HTML en la cadena atribuida.
Entiendo que bajo Mac OS X NSAttributedString
tiene un método initWithHTML:
pero esto fue solo una adición de Mac y no está disponible para iOS.
También sé que hay una pregunta similar a esta, pero no tenía respuestas, aunque volvería a intentar y ver si alguien ha creado una forma de hacerlo y, de ser así, si podrían compartirla.
¡Crear una cadena NSAttributedString desde HTML debe hacerse en el hilo principal!
Actualización: resulta que la representación HTML NSAttributedString depende de WebKit bajo el capó, y debe ejecutarse en el hilo principal o de vez en cuando se bloqueará la aplicación con un SIGTRAP .
Nuevo registro de bloqueo de Relic:
A continuación, se encuentra una extensión actualizada de Swift 2 String segura para subprocesos :
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)
}
}
}
}
Uso:
let html = "<center>Here is some <b>HTML</b></center>"
html.attributedStringFromHTML { attString in
self.bodyLabel.attributedText = attString
}
Salida:
Extensiones útiles
Inspirado por este hilo, un pod y el ejemplo ObjC de Erica Sadun en iOS Gourmet Cookbook p.80, escribí una extensión en String
y en NSAttributedString
para ir y venir entre HTML strings simples y NSAttributedStrings y viceversa - en GitHub here , que he encontrado útil.
Las firmas son (nuevamente, código completo en un enlace Gist, enlace arriba):
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 }
El uso de NSHTMLTextDocumentType es lento y es difícil controlar los estilos. Te sugiero que pruebes mi biblioteca, que se llama Atributika. Tiene su propio analizador de HTML muy rápido. También puede tener cualquier nombre de etiqueta y definir cualquier estilo para ellos.
Ejemplo:
let str = "<strong>Hello</strong> World!".style(tags:
Style("strong").font(.boldSystemFont(ofSize: 15))).attributedString
label.attributedText = str
Puede encontrarlo aquí https://github.com/psharanda/Atributika
En iOS 7, UIKit agregó un initWithData:options:documentAttributes:error: que puede inicializar un NSAtttributedString usando HTML, por ejemplo:
[[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding]
options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}
documentAttributes:nil error:nil];
Esta es una extensión de String
escrita en Swift para devolver una cadena HTML como 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
}
}
Usar,
label.attributedText = "<b>Hello</b> /u{2022} babe".htmlAttributedString()
En lo anterior, he agregado deliberadamente un unicode / u2022 para mostrar que representa unicode correctamente.
Una NSAttributedString
trivial: la codificación predeterminada que utiliza NSUTF16StringEncoding
es NSUTF16StringEncoding
(¡no UTF8!).
Hizo algunas modificaciones en la solución de Andrew y actualizó el código a Swift 3:
Este código ahora usa UITextView como self
y es capaz de heredar su fuente original, tamaño de fuente y color de texto
Nota: toHexString()
es una extensión desde 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)
}
}
}
}
Ejemplo de uso:
mainTextView.setAttributedStringFromHTML("<i>Hello world!</i>") { _ in }
La única solución que tiene ahora es analizar el HTML, crear algunos nodos con atributos de puntos / fuentes / etc. dados, luego combinarlos en un NSAttributedString. Es mucho trabajo, pero si se hace correctamente, puede ser reutilizable en el futuro.
La solución anterior es correcta.
[[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding]
options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}
documentAttributes:nil error:nil];
Pero la aplicación se bloqueará si la ejecuta en ios 8.1.2 o 3.
Para evitar el bloqueo, lo que puede hacer es: ejecutar esto en una cola. Para que siempre esté en el hilo principal.
Swift 3.0 Xcode 8 Version
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 :
Prueba esto :
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
}
}
Y para usar:
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()
Swift 4
- Inicializador de conveniencia NSAttributedString
- Sin guardias adicionales
- lanza un 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)
}
}
Uso
UILabel.attributedText = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!")