iphone objective-c cocoa-touch core-text nsattributedstring

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!).


Hay una here trabajo en progreso here por Oliver Drobnik en Github. Utiliza NSScanner para analizar HTML.


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!")