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.
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)
Il y a un travail en cours addition open source à NSAttributedString par Oliver Drobnik sur Github. Il utilise NSScanner pour l'analyse HTML.
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:
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:
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)
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!).
Swift 4
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!")
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
}
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 }
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.
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.
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()
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
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
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 }