Преобразование HTML в NSAttributedString в iOS
Я использую экземпляр UIWebView чтобы обработать некоторый текст и правильно его раскрасить, он дает результат в виде HTML, а не отображает его в UIWebView Я хочу, чтобы отобразить его с помощью Core Text С NSAttributedString.
Я умею создавать и рисовать NSAttributedString но я не уверен, как я могу конвертировать и сопоставлять HTML в атрибутивную строку.
Я понимаю, что под Mac OS X NSAttributedString есть initWithHTML: метод, но это было только добавление Mac и не доступно для усвн.
Я также знаю, что есть аналогичный вопрос, но у него не было ответов, хотя я бы попробовал еще раз и посмотрел, создал ли кто-нибудь способ сделать это, и если да, то если бы они могли поделиться им.
13 ответов:
в iOS 7, UIKit добавил initWithData: options:documentAttributes: error: метод, который может инициализировать Nsattributedstring с помощью HTML, например:
[[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)} documentAttributes:nil error:nil];В 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)
есть незавершенное производство дополнение с открытым исходным кодом к NSAttributedString Оливер Дробник в Github. Он использует NSScanner для синтаксического анализа HTML.
создание NSAttributedString из HTML должно быть сделано в основном потоке!
Update: оказывается, что nsattributedstring HTML-рендеринг зависит от WebKit под капотом, и должен быть запущен в основном потокеили это будет иногда сбой приложения с SIGTRAP.
New Relic crash log:
ниже обновлено потокобезопасным 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) } } } }использование:
let html = "<center>Here is some <b>HTML</b></center>" html.attributedStringFromHTML { attString in self.bodyLabel.attributedText = attString }выход:
расширение инициализатора Swift на NSAttributedString
Я хотел добавить это как расширение к
NSAttributedString, а неString. Я попробовал его как статическое расширение и инициализатор. Я предпочитаю инициализатор, который я включил ниже.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) } }пример
let html = "<b>Hello World!</b>" let attributedString = NSAttributedString(html: html)
это
Stringрасширение, написанное в Swift, чтобы вернуть строку HTML как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 } }использовать
label.attributedText = "<b>Hello</b> \u{2022} babe".htmlAttributedString()в приведенном выше примере я намеренно добавил unicode \u2022, чтобы показать, что он правильно отображает unicode.
тривиальный: кодировка по умолчанию, что
NSAttributedStringиспользуетNSUTF16StringEncoding(не 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 }
единственное решение, которое у вас есть прямо сейчас, - это проанализировать HTML, создать некоторые узлы с заданными атрибутами point/font/etc, а затем объединить их вместе в NSAttributedString. Это много работы, но если все сделано правильно, может быть многоразовым в будущем.
сделал некоторые изменения на Андрей'S решение и обновить код до Swift 3:
этот код теперь использует UITextView как
selfи возможность наследовать его оригинальный шрифт, размер шрифта и цвет текстаПримечание:
toHexString()- Это расширение от здесь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) } } } }пример использования:
mainTextView.setAttributedStringFromHTML("<i>Hello world!</i>") { _ in }
Swift 4
- NSAttributedString удобство инициализатор
- без дополнительных охранников
- генерирует ошибку
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) } }использование
UILabel.attributedText = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!")
приведенное выше решение является правильным.
[[NSAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)} documentAttributes:nil error:nil];но приложение wioll аварии, если вы используете его на ios 8.1,2 или 3.
чтобы избежать аварии, что вы можете сделать это : запустить в очереди. Так что он всегда будет на главной нити.
использование NSHTMLTextDocumentType является медленным и трудно контролировать стили. Я предлагаю вам попробовать мою библиотеку, которая называется атрибутика. Он имеет свой собственный очень быстрый парсер HTML. Также вы можете иметь любые имена тегов и определить подходящий стиль для них.
пример:
let str = "<strong>Hello</strong> World!".style(tags: Style("strong").font(.boldSystemFont(ofSize: 15))).attributedString label.attributedText = strвы можете найти его здесь https://github.com/psharanda/Atributika
Swift 3:
попробуй такое: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 } }использование:
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()
Полезные Расширения
вдохновленный этой нитью, стручком и примером ObjC Эрики Садун в iOS Gourmet Cookbook p. 80, я написал расширение на
StringиNSAttributedStringчтобы перейти туда и обратно между HTML plain-strings и NSAttributedStrings и наоборот -- на GitHub здесь, который я нашел полезным.The подписи are (опять же, полный код в Gist, ссылка выше):
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 }


Comments