Konwertuj HTML na NSAttributedString w systemie iOS

Używam instancji UIWebView, aby przetworzyć jakiś tekst i poprawnie go pokolorować, daje wynik jako HTML, ale zamiast wyświetlać go w UIWebView chcę wyświetlić go za pomocą Core Text z NSAttributedString.

Jestem w stanie utworzyć i narysować NSAttributedString, ale nie jestem pewien, jak mogę przekonwertować i mapować HTML na przypisany ciąg znaków.

Rozumiem, że pod Mac OS X NSAttributedString ma metodę initWithHTML:, ale był to dodatek tylko dla Mac i nie jest dostępny dla iOS.

Wiem też, że tam jest podobne pytanie do tego, ale nie miał odpowiedzi, myślałem, że spróbuję jeszcze raz i zobaczyć, czy ktoś stworzył sposób, aby to zrobić, a jeśli tak, czy może się nim podzielić.

Author: Joshua, 2010-11-18

13 answers

W systemie iOS 7 UIKit dodał metodę initWithData:options:documentAttributes:error:, która może zainicjować NSAtttributedString za pomocą HTML, np:

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

W Języku 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)
 261
Author: pix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-31 12:53:39

[[0]}istnieje dodatek open source do NSAttributedString autorstwa Olivera Drobnika na Githubie. Używa NSScanner do parsowania HTML.

 42
Author: Ingve,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-09 12:58:16

Tworzenie NSAttributedString z HTML musi być wykonane w głównym wątku!

Update: okazuje się, że renderowanie HTML NSATTRIBUTEDSTRING zależy od Webkitu pod maską i musi być uruchomione w głównym wątku lub od czasu do czasu spowoduje awarię aplikacji przy użyciu SIGTRAP .

New Relic crash log:

Tutaj wpisz opis obrazka

Poniżej znajduje się zaktualizowany thread-safe Swift 2 String rozszerzenie:

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

Użycie:

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

Wyjście:

Tutaj wpisz opis obrazka

 22
Author: Andrew Schreiber,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-24 20:56:02

Swift initializer extension on NSAttributedString

Moim zamiarem było dodanie tego jako rozszerzenia do NSAttributedString, a nie String. Próbowałem to jako rozszerzenie statyczne i inicjalizator. Wolę inicjalizator, który jest tym, co zawarłem poniżej.

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

Przykład

let html = "<b>Hello World!</b>"
let attributedString = NSAttributedString(html: html)
 13
Author: Mobile Dan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-03 07:50:03

Jest to rozszerzenie String napisane w języku Swift, które zwraca łańcuch HTML jako 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
    }
}

Do użycia,

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

W powyższym przykładzie celowo dodałem unicode \u2022, aby pokazać, że poprawnie renderuje unicode.

Trywialne: domyślnym kodowaniem używanym przez NSAttributedString jest NSUTF16StringEncoding (nie UTF8!).

 7
Author: samwize,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-25 03:23:33

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
Author: Fsousa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-28 18:19:48

Jedynym rozwiązaniem, jakie masz w tej chwili, jest analiza HTML, zbudowanie kilku węzłów z podanymi atrybutami point / font / etc, a następnie połączenie ich w nsattributedstring. To dużo pracy, ale jeśli zostanie wykonana poprawnie, może być wielokrotnego użytku w przyszłości.

 4
Author: jer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-18 17:42:03

Dokonałem modyfikacji na rozwiązaniu Andrew i zaktualizowałem kod do Swift 3:

Ten kod używa teraz UITextView jako {[2] } i może dziedziczyć oryginalną czcionkę, rozmiar czcionki i kolor tekstu

Uwaga: toHexString() jest rozszerzeniem z tutaj

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

Przykładowe użycie:

mainTextView.setAttributedStringFromHTML("<i>Hello world!</i>") { _ in }
 4
Author: Yifei He,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:34

Swift 4


  • nsattributedstring convenience initializer
  • bez dodatkowych osłon
  • wyrzuca błąd

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

}

Użycie

UILabel.attributedText = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!")
 4
Author: AamirR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-30 19:43:53

Powyższe rozwiązanie jest poprawne.

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

Ale aplikacja wioll zawiesza się, jeśli używasz jej na ios 8.1,2 lub 3.

Aby uniknąć awarii, możesz: uruchomić to w kolejce. Aby zawsze było na głównym wątku.

 2
Author: Nitesh Kumar Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-30 07:17:41

Używanie nshtmltextdocumenttype jest powolne i trudno jest kontrolować style. Proponuję wypróbować moją bibliotekę, która nazywa się Atributika. Posiada własny bardzo szybki parser HTML. Możesz także mieć dowolne nazwy znaczników i definiować dla nich dowolny styl.

Przykład:

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

label.attributedText = str

Znajdziesz go tutaj https://github.com/psharanda/Atributika

 2
Author: Pavel Sharanda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-22 19:51:58

Swift 3:
Try this :

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

I za pomocą:

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
Author: reza_khalafi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-26 22:46:50

Pomocne Rozszerzenia

Zainspirowany tym wątkiem, a pod, i Erica Sadun ' s ObjC przykład w iOS Gourmet Cookbook str. 80, napisałem rozszerzenie na String i na NSAttributedString aby przejść tam iz powrotem między HTML plain-strings i NSAttributedStrings i odwrotnie -- na GitHub tutaj , które uznałem za pomocne.

Podpisy to (ponownie, Pełny kod w Gist, link powyżej):

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
Author: AmitaiB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-19 12:02:53