Pregunta

Estoy utilizando una instancia de UIWebView para procesar un texto y el color correctamente, da el resultado como HTML, pero en vez de mostrarla en el UIWebView quiero mostrarlo usando Core Text con un NSAttributedString.

Soy capaz de crear y dibujar la NSAttributedString pero no estoy seguro de cómo puedo convertir y mapear el HTML en la cadena con atributos.

Me entender que en Mac OS X NSAttributedString tiene un método initWithHTML: pero esto fue una única Mac suma y no está disponible para iOS.

también sé que hay una pregunta similar a esta pero no tenía respuestas, aunque yo lo intentaría de nuevo y ver si alguien ha creado una manera de hacer esto y si es así, si pudieran compartirlo.

¿Fue útil?

Solución

En iOS 7, UIKit añade un initWithData:options:documentAttributes:error: método que puede inicializar un NSAtttributedString el uso de HTML, por ejemplo:

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

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

Otros consejos

Hay una obra en curso adición de fuente abierta a NSAttributedString por Oliver Drobnik en Github. Utiliza NSScanner para el análisis de HTML.

Creación de un NSAttributedString de HTML se debe hacer en el hilo principal!

Actualización: Resulta que NSAttributedString de representación HTML depende de WebKit bajo el capó, y se debe ejecutar en el hilo principal o de vez en cuando bloquee la aplicación con un SIGTRAP .

New Relic registro de bloqueo:

 introducir descripción de la imagen aquí

A continuación se muestra un thread-safe Swift 2 extensión actualizado de la secuencia:

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:

 introducir descripción de la imagen aquí

Swift inicializador extensión en NSAttributedString

Mi inclinación era añadir esto como una extensión a NSAttributedString en lugar de String. Lo probé como una extensión y un inicializador estático. Yo 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)

Esta es una extensión 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
    }
}

Para utilizar,

label.attributedText = "<b>Hello</b> \u{2022} babe".htmlAttributedString()

En lo anterior, he añadido un propósito Unicode \ u2022 para demostrar que hace Unicode correctamente.

Un trivial:. El valor por defecto de codificación que es NSAttributedString usos NSUTF16StringEncoding (! No UTF8)

Swift 3,0 Xcode 8 Versión

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 4


  • NSAttributedString conveniencia inicializador
  • Sin guardias adicionales
  • lanza de 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!")

La única solución que tenemos ahora es analizar el código HTML, construir algunos nodos con atributos dados Punto / fuente / etc, y luego combinarlos en un NSAttributedString. Es mucho trabajo, pero si se hace correctamente, puede ser reutilizable en el futuro.

hecho algunas modificaciones en la solución Andrew 's y actualizar el código para Swift 3:

Este código utilizan ahora como UITextView self y capaces de heredar de su fuente original, tamaño de fuente y color del texto

Nota: toHexString() es la extensión del aquí

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 solución anterior es correcta.

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

embargo, la aplicación wioll bloquee si está ejecutando en ios 8.1,2 ó 3.

Para evitar el choque de lo que puede hacer es: ejecutar esto en una cola. De modo que siempre sea el hilo principal.

El uso de NSHTMLTextDocumentType es lento y es difícil de estilos de control. Le sugiero que pruebe mi biblioteca que se llama Atributika. Tiene su propio analizador de HTML muy rápido. También se 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

Se puede encontrar aquí https://github.com/psharanda/Atributika

Swift 3 :
Probar

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

Extensiones útiles

Inspirado en este hilo, una vaina, y el ejemplo ObjC de Erica Sadun en IOS Gourmet Cookbook p.80, me escribió una extensión en String y en NSAttributedString ir y venir entre HTML plano-secuencias y NSAttributedStrings y viceversa - en GitHub aquí , que he encontrado útiles.

La firmas son (de nuevo, el código completo en un Gist, enlace de 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 }

con la fuente

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

Alternativamente, puede utilizar las versiones este se deriva de y conjunto fuente en UILabel después de establecer attributedString

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top