Domanda

Io sto usando un'istanza di UIWebView per elaborare un testo e il colore correttamente, dà il risultato come HTML, ma piuttosto che visualizzare nel UIWebView voglio visualizzarlo utilizzando Core Text con un NSAttributedString.

Sono in grado di creare e disegnare il NSAttributedString ma non sono sicuro come posso convertire e mappare il codice HTML nella stringa attribuito.

Mi rendo conto che in Mac OS X NSAttributedString ha un metodo initWithHTML: ma questo era un solo Mac addizione e non è disponibile per iOS.

so anche che c'è una domanda simile a questo, ma non ha avuto risposte, anche se vorrei provare ancora e vedere se qualcuno ha creato un modo per fare questo e in caso affermativo, se potessero condividerlo.

È stato utile?

Soluzione

In iOS 7, UIKit ha aggiunto un initWithData:options:documentAttributes:error: metodo che può inizializzare un NSAtttributedString utilizzando HTML, ad esempio:

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

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

Altri suggerimenti

C'è un work-in-progress open source Oltre a NSAttributedString da Oliver Drobnik a Github. Esso utilizza NSScanner per HTML parsing.

Creazione di un NSAttributedString da HTML deve essere fatto sul thread principale!

Aggiornamento: Si scopre che NSAttributedString rendering HTML dipende WebKit sotto il cofano, e deve essere eseguito sul thread principale oppure a volte in crash l'applicazione con un SIGTRAP .

Nuovo Relic crash log:

 entrare descrizione dell'immagine qui

Di seguito è una versione aggiornata thread-safe Swift 2 estensione Stringa:

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

Utilizzo:

let html = "<center>Here is some <b>HTML</b></center>"
html.attributedStringFromHTML { attString in
    self.bodyLabel.attributedText = attString
}

Output:

 entrare descrizione dell'immagine qui

Swift initializer estensione sul NSAttributedString

La mia inclinazione è stato quello di aggiungere questo come un'estensione NSAttributedString piuttosto che String. Ho provato come estensione statico e un inizializzatore. Io preferisco l'inizializzazione che è quello che ho riportato qui di seguito.

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

Esempio

let html = "<b>Hello World!</b>"
let attributedString = NSAttributedString(html: html)

Questa è un'estensione String scritto in Swift per restituire una stringa HTML come 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
    }
}

Per usare,

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

In quanto sopra, ho volutamente aggiunto un'unicode \ u2022 per dimostrare che rende correttamente unicode.

Un banale:. Il valore di default codifica che utilizza NSAttributedString è NSUTF16StringEncoding (! Non UTF8)

Swift 3.0 Xcode 8 Versione

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 convenienza initializer
  • Senza guardie aggiuntivi
  • genera un errore

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

}

Utilizzo

UILabel.attributedText = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!")

L'unica soluzione che hai adesso è quello di analizzare il codice HTML, costruire alcuni nodi con punto / font / etc attributi, poi combinarli insieme in un NSAttributedString. E 'un sacco di lavoro, ma se fatto correttamente, può essere riutilizzabile in futuro.

fatto qualche modifica sul soluzione Andrew s 'e aggiornare il codice di Swift 3:

Il codice ora utilizzare UITextView come self e in grado di ereditare il suo carattere originale, dimensione del carattere e il colore del testo

Nota: toHexString() è l'estensione da qui

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

Esempio di utilizzo:

mainTextView.setAttributedStringFromHTML("<i>Hello world!</i>") { _ in }

La soluzione di cui sopra è corretta.

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

Ma l'applicazione wioll in crash se si esegue su ios 8.1,2 o 3.

Per evitare l'incidente che cosa si può fare è: eseguire questo in una coda. In modo che sia sempre sul thread principale.

Utilizzo di NSHTMLTextDocumentType è lento ed è difficile per gli stili di controllo. Vi suggerisco di provare la mia libreria che si chiama Atributika. Ha un proprio parser HTML molto veloce. Inoltre è possibile avere i nomi dei tag e definire qualsiasi stile per loro.

Esempio:

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

label.attributedText = str

È possibile trovare qui https://github.com/psharanda/Atributika

Swift 3 :
Prova questo :

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

E per l'utilizzo di:

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

Estensioni utili

Ispirato da questa discussione, un baccello, e l'esempio objC di Erica Sadun in iOS Gourmet Cookbook p.80, ho scritto un'estensione su String e NSAttributedString per andare avanti e indietro tra HTML plain-stringhe e NSAttributedStrings e viceversa - su GitHub qui , che ho trovato utile.

Il firme sono (di nuovo, pieno di codice in un Gist, link qui sotto):

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 font

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

In alternativa è possibile utilizzare le versioni questo è stato derivato da e set carattere sul UILabel dopo aver impostato attributedString

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top