Konvertieren von HTML zu NSAttributedString in iOS
-
26-09-2019 - |
Frage
Ich bin eine Instanz von UIWebView
mit etwas Text zu verarbeiten und es richtig Farbe, es gibt das Ergebnis als HTML, sondern als in den UIWebView
Anzeige Ich mag es angezeigt mit einem Core Text
mit NSAttributedString
.
Ich kann den NSAttributedString
erstellen und zu zeichnen, aber ich bin nicht sicher, wie ich umwandeln kann und ordnen Sie den HTML-Code in den zurück String.
Ich verstehe, dass unter Mac OS X NSAttributedString
eine initWithHTML:
Methode hat aber dies war ein Mac nur hinaus und ist für iOS nicht verfügbar ist.
Ich weiß auch, dass es eine ähnliche Frage dazu ist, aber es hatte keine Antworten, ich obwohl ich wieder versuchen würde, und sehen, ob jemand eine Möglichkeit, dies zu tun, geschaffen hat und wenn ja, ob sie es teilen konnten.
Lösung
In iOS 7, UIKit hinzugefügt, um ein initWithData:options:documentAttributes:error:
Methode, die eine NSAtttributedString
mit HTML initialisieren kann, zum Beispiel:
[[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)
Andere Tipps
Es ist ein work-in-progress Open-Source neben NSAttributedString von Oliver Drobnik bei Github. Es verwendet NSScanner für HTML-Analyse.
Erstellen von HTML ein NSAttributedString muss auf dem Haupt-Thread ausgeführt werden!
Update: Es stellt sich heraus, dass NSAttributedString HTML-Rendering auf WebKit unter der Haube ab, und muss auf dem Haupt-Thread ausgeführt wird oder es wird gelegentlich die App mit einem SIGTRAP Absturz .
New Relic Crash-Protokoll:
eingebenIm Folgenden finden Sie eine aktualisierte thread-safe Swift 2 String-Erweiterung:
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)
}
}
}
}
Verbrauch:
let html = "<center>Here is some <b>HTML</b></center>"
html.attributedStringFromHTML { attString in
self.bodyLabel.attributedText = attString
}
Ausgabe:
eingebenSwift initializer Erweiterung auf NSAttributedString
war meine Neigung dies als eine Erweiterung hinzuzufügen eher zu NSAttributedString
als String
. Ich habe versucht, es als eine statische Dehnung und Initialisierer. Ich ziehe den initializer das ist, was ich unten aufgeführt haben.
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)
}
}
Beispiel:
let html = "<b>Hello World!</b>"
let attributedString = NSAttributedString(html: html)
Dies ist eine String
Erweiterung in Swift geschrieben, um eine HTML-Zeichenfolge als NSAttributedString
zurückzukehren.
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
}
}
verwenden,
label.attributedText = "<b>Hello</b> \u{2022} babe".htmlAttributedString()
In der oben habe ich absichtlich eine Unicode \ u2022 zu zeigen, fügte hinzu, dass es Unicode macht richtig.
A trivial. Die Standard-Codierung, die NSAttributedString
Anwendungen ist NSUTF16StringEncoding
(! Nicht UTF8)
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 4
- NSAttributedString Bequemlichkeit initializer
- Ohne zusätzliche Wachen
- wirft Fehler
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)
}
}
Verwendung
UILabel.attributedText = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!")
Die einzige Lösung, die Sie jetzt haben, ist die HTML zu analysieren, einige Knoten mit bestimmten Punkt aufbauen / font / etc Attribute, so dass sie dann zusammen verbinden sich zu einem NSAttributedString. Es ist eine Menge Arbeit, aber wenn es richtig gemacht, kann in Zukunft wiederverwendbar sein.
Made einige Änderungen auf Andrew 's Lösung und aktualisieren Sie den Code Swift 3:
Dieser Code jetzt UITextView als self
und in der Lage verwenden, seine ursprüngliche Schriftart, Schriftgröße und Textfarbe erben
Hinweis: toHexString()
ist Erweiterung von hier
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)
}
}
}
}
Beispiel Nutzung:
mainTextView.setAttributedStringFromHTML("<i>Hello world!</i>") { _ in }
Die obige Lösung ist richtig.
[[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding]
options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}
documentAttributes:nil error:nil];
Aber die App wioll abstürzen, wenn Sie es auf ios laufen 8.1,2 oder 3.
den Absturz zu vermeiden, was Sie tun können, ist: diese in einer Warteschlange laufen. So dass es immer auf Hauptthread sein.
von NSHTMLTextDocumentType Verwendung ist langsam und es ist schwer zu kontrollieren Stile. Ich schlage vor, Sie meine Bibliothek, um zu versuchen, die Atributika genannt wird. Es hat seinen eigenen sehr schnell HTML-Parser. Auch können Sie alle Tag-Namen haben und definieren jeden Stil für sie.
Beispiel:
let str = "<strong>Hello</strong> World!".style(tags:
Style("strong").font(.boldSystemFont(ofSize: 15))).attributedString
label.attributedText = str
Sie können es hier finden https://github.com/psharanda/Atributika
Swift 3 :
Versuchen Sie, diese :
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
}
}
Und für die Verwendung:
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()
Hilfreiche Erweiterungen
durch diesen Thread inspiriert, eine Hülse, und Erica Sadun des ObjC Beispiel in iOS Gourmet-Kochbuch S.80, schrieb ich eine Erweiterung auf String
und auf NSAttributedString
hin und her zwischen HTML Plain-Strings und NSAttributedStrings und umgekehrt zu gehen - hier , die ich nützlich gefunden haben.
Die Signaturen sind (wieder vollständige Code in einem Gist, Link oben):
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 }
mit Schrift
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)
}
}
Alternativ können Sie die Möglichkeiten nutzen dies aus und Satz abgeleitet wurde Schriftart auf UILabel nach der Einstellung attributedString