web-dev-qa-db-fra.com

Conversion de HTML en chaîne NSAttributed dans iOS

J'utilise une instance de UIWebView pour traiter du texte et le colorer correctement. Il donne le résultat au format HTML mais plutôt que de l'afficher dans la UIWebView Je souhaite l'afficher à l'aide de Core Text avec une NSAttributedString

Je suis capable de créer et dessiner la NSAttributedString mais je ne sais pas comment je peux convertir et mapper le code HTML dans la chaîne attribuée.

Je comprends que, sous Mac OS X, NSAttributedString a une méthode initWithHTML: mais il s’agit d’un ajout réservé aux Mac et n’est pas disponible pour iOS.

Je sais aussi qu’il ya une question similaire à celle-ci mais qu’il n’y avait pas de réponse. Je voudrais bien essayer de nouveau et voir si quelqu'un a créé un moyen de le faire et si oui, s’ils pouvaient la partager.

135
Joshua

Dans iOS 7, UIKit a ajouté une méthode initWithData: options: documentAttributes: error: qui peut initialiser une chaîne NSAtttributed à l'aide de HTML, par exemple:

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

En rapide:

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)
268
pix

Il y a un travail en cours addition open source à NSAttributedString par Oliver Drobnik sur Github. Il utilise NSScanner pour l'analyse HTML.

42
Ingve

La création d'un NSAttributedString à partir de HTML doit être effectuée sur le thread principal!

Mise à jour: il s'avère que le rendu HTML de NSAttributedString dépend de WebKit et que doit être exécuté sur le thread principal _ ​​sinon l'application plantera occasionnellement avec un SIGTRAP.

Nouveau journal de crash de Relic:

 enter image description here

Vous trouverez ci-dessous une extension mise à jour de la chaîne thread-safe 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)
            }
        }
    }
}

Usage:

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

Sortie:

 enter image description here

22
Andrew Schreiber

Extension d'initialiseur Swift sur NSAttributedString

J'avais tendance à ajouter ceci comme une extension de NSAttributedString plutôt que String. Je l'ai essayé en tant qu'extension statique et initialiseur. Je préfère l'initialiseur qui est ce que j'ai inclus ci-dessous.

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

Exemple

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

Il s'agit d'une extension String écrite en Swift pour renvoyer une chaîne HTML sous la forme 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
    }
}

Utiliser, 

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

Dans ce qui précède, j’ai volontairement ajouté un unicode pour montrer qu’il restitue bien l’unicode. 

Trivial: Le codage par défaut utilisé par NSAttributedString est NSUTF16StringEncoding (pas UTF8!).

7
samwize

Swift 4


  • NSAttributedString initializer de commodité
  • Sans gardes supplémentaires
  • jette l'erreur

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

}

Utilisation

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

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

Apportez quelques modifications à la solution de Andrew et mettez à jour le code vers Swift 3:

Ce code utilise désormais UITextView en tant que self et peut hériter de la police, de la taille et de la couleur du texte d'origine.

Remarque: toHexString() est une extension de ici

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

Exemple d'utilisation:

mainTextView.setAttributedStringFromHTML("<i>Hello world!</i>") { _ in }
4
He Yifei 何一非

La seule solution que vous avez actuellement consiste à analyser le code HTML, à créer des nœuds avec des attributs point/font/etc donnés, puis à les combiner en une chaîne NSAttributed. C'est beaucoup de travail, mais s'il est fait correctement, il peut être réutilisé à l'avenir.

4
jer

La solution ci-dessus est correcte. 

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

Mais l'application tombe en panne si vous l'exécutez sur iOS 8.1,2 ou 3.

Pour éviter le crash, ce que vous pouvez faire est: exécutez ceci dans une file d'attente. Pour qu'il soit toujours sur le fil principal.

2
Nitesh Kumar Singh

Swift 3 :
Essaye ça

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

Et pour utiliser: 

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

L'utilisation de NSHTMLTextDocumentType est lente et il est difficile de contrôler les styles. Je vous suggère d'essayer ma bibliothèque qui s'appelle Atributika. Il possède son propre analyseur HTML très rapide. Vous pouvez aussi avoir n'importe quel nom de tag et définir n'importe quel style pour eux.

Exemple:

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

label.attributedText = str

Vous pouvez le trouver ici https://github.com/psharanda/Atributika

2
Pavel Sharanda

avec police

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

sinon, vous pouvez utiliser les versions dont cela a été dérivé et définir la police sur UILabel après la définition de attributString

0
Anton Tropashko

Extensions utiles

Inspiré par ce fil, un pod et l'exemple ObjC d'Erica Sadun dans iOS Gourmet Cookbook p.80, j'ai écrit une extension sur String et sur NSAttributedString pour faire la navette entre les chaînes de caractères HTML et NSAttributedStrings et inversement - sur GitHub ici , que j'ai trouvé utile.

Les signatures sont (encore une fois, le code complet dans un Gist, lien ci-dessus):

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