İOS'ta HTML'yi NSAttributedString'e dönüştürme


151

UIWebViewBazı metni işlemek ve doğru renklendirmek için bir örneğini kullanıyorum , sonucu HTML olarak verir, ancak bir ile UIWebViewkullanarak görüntülemek istiyorum .Core TextNSAttributedString

Ben oluşturmak ve çizmek mümkün NSAttributedStringama nasıl dönüştürmek ve ilişkilendirilmiş dize HTML eşleme emin değilim.

Mac OS X altında NSAttributedStringbir initWithHTML:yöntem olduğunu anlıyorum, ancak bu sadece bir Mac ilavesi ve iOS için mevcut değil.

Ben de buna benzer bir soru olduğunu biliyorum ama hiçbir cevap yoktu, ama ben tekrar denemek ve kimse bunu yapmak için bir yol oluşturmak olup olmadığını görmek ve eğer öyleyse, eğer paylaşmak eğer.


2
HTML için NSAttributedString-Additions kütüphanesi yeniden adlandırıldı ve aynı yazar tarafından bir çerçeveye alındı. Şimdi DTCoreText olarak adlandırılıyor ve bir grup Temel Metin düzeni sınıfı içeriyor. Burada
Brian Douglas Moakley

Yanıtlar:


290

İOS 7'de UIKit , HTML kullanarak initWithData:options:documentAttributes:error:başlatabilecek bir yöntem ekledi NSAttributedString, örneğin:

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

Swift'te:

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)

28
Bazı nedenlerden dolayı, NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType seçeneği kodlamanın gerçekten çok uzun sürmesine neden oluyor :(
Arie Litovsky

14
Çok kötü NSHTMLTextDocumentType, NSRange ile öznitelikleri ayarlamaktan (tam anlamıyla) ~ 1000x daha yavaş. (Bir cesur etiketle kısa bir etiket çıkardı.)
Jason Moore

6
Bir arka plan iş parçacığından kullanmak istiyorsanız, bu yöntemle NSHTMLTextDocumentType yapamıyorsanız unutmayın. İOS 7 ile bile, HTML oluşturma için TextKit kullanmaz. Ingve tarafından önerilen DTCoreText kütüphanesine bir göz atın.
TJez

2
Muhteşem. Sadece bir düşünce, muhtemelen [NSNumber numberWithInt: NSUTF8StringEncoding] @ (NSUTF8StringEncoding) olarak yapabilirsiniz, değil mi?
Jarsen

15
Bunu yapıyordum, ancak iOS 8'de dikkatli olun. Acıklı bir şekilde yavaş, birkaç yüz karakter için bir saniyeye yakın. (İOS 7'de neredeyse anlıktı.)
Norman

43

Github'da Oliver Drobnik tarafından NSAttributedString'e devam eden bir açık kaynak eki var. HTML ayrıştırma için NSScanner kullanır.


İOS 4.3'ün minimum dağıtımını gerektirir :( Hiç-daha az, çok etkileyici.
Oh Danny Boy

3
@Lirik Sizin için Overkill belki ama başka biri için mükemmel yani yorumunuz en azından biraz yararlı değil.
wuf810

3
Lütfen bu projenin gerektirdiğinin açık kaynaklı olduğunu ve standart 2 maddeli BSD lisansı kapsamında olduğunu unutmayın. Bu, Cocoanetics'ten bu kodun orijinal yazarı olarak bahsetmeniz ve LICENSE metnini uygulamanızın içinde yeniden oluşturmanız gerektiği anlamına gelir.
dulgan

28

HTML'den bir NSAttributedString oluşturmak ana iş parçacığında yapılmalıdır!

Güncelleme: NSAttributedString HTML oluşturmanın kaputun altındaki WebKit'e bağlı olduğu ve ana iş parçacığında çalıştırılması gerektiği veya bazen bir SIGTRAP ile uygulamanın çökmesine neden olacağı ortaya çıkıyor .

Yeni Relic kilitlenme günlüğü:

resim açıklamasını buraya girin

Aşağıda güncellenmiş bir iş parçacığı için güvenli Swift 2 String uzantısı bulunmaktadır:

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

Kullanımı:

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

Çıktı:

resim açıklamasını buraya girin


Andrew. Bu iyi çalışıyor. Ben bu yaklaşım ile gidecek eğer benim UITextView işlemek zorunda olayların tüm kısa bilmek istedim. HTML'de bulunan Takvim etkinliği, Arama, E-posta, Web sitesi bağlantısı vb. Umarım UITextView olayları UILabel ile karşılaştırır.
harshit2811

Yukarıdaki yaklaşım yalnızca biçimlendirme için iyidir. Olay işleme gerekiyorsa TTTAttributedLabel kullanmanızı öneririz .
Andrew Schreiber

NSAttributedString tarafından kullanılan varsayılan kodlama NSUTF16StringEncoding'dir (UTF8 değil!). Bu yüzden bu işe yaramayacak. En azından benim durumumda!
Ümit Kaya

Bu kabul edilen çözüm olmalıdır. Bir arka plan iş parçacığında bir HTML dizesi görüşmesi yapmak , sonunda sınamalar sırasında ve sık sık sınanır.
ratsimihah

21

NSAttributedString üzerinde hızlı başlatıcı uzantısı

Eğilimim bunu NSAttributedStringyerine bir uzantı olarak eklemekti String. Statik bir uzantı ve bir başlatıcı olarak denedim. Başlatıcıyı aşağıya tercih ederim.

Hızlı 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)
}

Hızlı 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)
}
}

Misal

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

merhaba dünyanın böyle olmasını istiyorum <p><b><i>hello</i> </b> <i>world</i> </p>
Uma Madhavi

Ve bazı LOC kaydet yerine guard ... NSMutableAttributedString(data:...göre try self.init(data:...(ve eklemek throwsinit)
NYG

ve nihayet işe yaramıyor - metin rastgele yazı tipi boyutu
kazanıyor

2
Verilerin UTF-8 ile kodunu çözüyorsunuz, ancak UTF-16 ile
kodladınız

11

Bu, StringHTML dizesini olarak döndürmek için Swift'te yazılmış bir uzantıdır 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
    }
}

Kullanmak,

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

Yukarıda, bilerek unicode doğru şekilde oluşturduğunu göstermek için bir unicode \ u2022 ekledim.

Önemsiz: NSAttributedStringKullanan varsayılan kodlama NSUTF16StringEncoding(UTF8 değil!).


UTF16 günümü kurtardı, Teşekkürler samwize!
Yueyu

UTF16 günümü kurtardı, Teşekkürler samwize!
Yueyu

6

Andrew'un çözümünde bazı değişiklikler yaptı ve kodu Swift 3'e güncelleyin:

Bu kod artık UITextView özelliğini kullanıyor selfve orijinal yazı tipini, yazı tipi boyutunu ve metin rengini devralabiliyor

Not: buradantoHexString() uzatma

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

Örnek kullanım:

mainTextView.setAttributedStringFromHTML("<i>Hello world!</i>") { _ in }

5

Swift 3.0 Xcode 8 Sürümü

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

Hızlı 4


  • NSAttributedString kolaylık başlatıcısı
  • Ekstra koruma olmadan
  • hata veriyor

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

}

kullanım

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

Sen benim günümü kurtar. Teşekkür ederim.
pkc456

@ pkc456 meta.stackexchange.com/questions/5234/… , upvote :) teşekkürler!
AamirR

Yazı tipi boyutunu ve yazı tipi ailesini nasıl ayarlayabilirim?
kirqe

Bu, Mobile Dan tarafından önerilenden çok daha iyi, çünkü self.init (attributedString: attributedString) ile yedek bir kopya içermiyor
siyanür

4

Şu anda sahip olduğunuz tek çözüm HTML'yi ayrıştırmak, verilen nokta / yazı tipi / etc özniteliklerine sahip bazı düğümler oluşturmak ve bunları bir NSAttributedString'de birleştirmektir. Çok fazla iş var, ancak doğru şekilde yapılırsa gelecekte tekrar kullanılabilir.


1
HTML XHTML-Strict ise, ayrıştırmaya yardımcı olması için NSXMLDOcument ve arkadaşlarını kullanabilirsiniz.
Dylan Lukes

Belirtilen özelliklere sahip düğümleri oluşturmayı nasıl öneririm?
Joshua

2
Bu bir uygulama detayı. HTML'yi ayrıştırırsanız da, her etiket için yazı tipi adı, boyutu vb. Gibi şeyleri belirten her bir özelliğe erişiminiz olur. Bu bilgileri, atıfta bulunulan metne özellik olarak eklemeniz gereken ilgili ayrıntıları depolamak için kullanabilirsiniz . Genellikle, böyle bir görevi yerine getirmeden önce ayrıştırma hakkında bilgi sahibi olmanız gerekir.
jer

2

Yukarıdaki çözüm doğrudur.

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

Ancak ios 8.1,2 veya 3 üzerinde çalıştırıyorsanız uygulama wioll çöküyor.

Çökmeyi önlemek için yapabileceğiniz şey: bunu bir kuyrukta çalıştırın. Böylece her zaman ana iplikte olur.


@alecex Aynı problemle karşılaştım! uygulaması iOS 8.1, 2, 3'te kilitleniyor. Ancak iOS 8.4 veya sonraki sürümlerde iyi olacak. Bundan nasıl kaçınacağınızı ayrıntılı olarak açıklayabilir misiniz? ya da etrafında herhangi bir çalışma var mı, yoksa yöntemler kullanılabilir mi?
Güçlü

Bunu yapmak için çok kolay ve sezgisel bir yolu olan AppKit'ten yöntemleri kopyalayarak hızlı bir kategori yaptım. Apple neden eklemedi beni: github.com/cguess/NSMutableAttributedString-HTML
CGuess

2

NSHTMLTextDocumentType kullanımı yavaştır ve stilleri kontrol etmek zordur. Atributika adlı kütüphanemi denemenizi öneririm. Kendi çok hızlı HTML ayrıştırıcısına sahiptir. Ayrıca herhangi bir etiket adınız olabilir ve bunlar için herhangi bir stil tanımlayabilirsiniz.

Misal:

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

label.attributedText = str

Burada bulabilirsiniz https://github.com/psharanda/Atributika


2

Swift 3 : Şunu
deneyin :

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

Ve kullanmak için:

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

0

Yardımcı Uzantılar

Bu konuya, bir elmanın ve iOS'ta Gurme Cookbook s.80 içinde Erica Sadun'ın objc örnek esinlenerek, ben bir uzantı yazdı Stringve üzerinde NSAttributedStringtersi ileri geri HTML düz dizeleri ve NSAttributedStrings ve yardımcısı arasındaki gitmek - GitHub üzerinde burada , hangi Yararlı buldum.

İmzalar (a Gist yine, tam kod yukarıdaki link) şunlardır:

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

yazı tipi ile

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

alternatif olarak bunun türetilmiş sürümlerini kullanabilir ve attributedString ayarladıktan sonra UILabel'de yazı tipini ayarlayabilirsiniz


0

Yerleşik dönüşüm, .forgroundColor ile başka bir şeye ayarlanmış bir öznitelik sözlüğü iletseniz bile, metin rengini her zaman UIColor.black olarak ayarlar. İOS 13'te KOYU modunu desteklemek için, uzantının bu sürümünü NSAttributedString'de deneyin.

extension NSAttributedString {
    internal convenience init?(html: String)                    {
        guard 
            let data = html.data(using: String.Encoding.utf16, allowLossyConversion: false) else { return nil }

        let options : [DocumentReadingOptionKey : Any] = [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
        ]

        guard
            let string = try? NSMutableAttributedString(data: data, options: options,
                                                 documentAttributes: nil) else { return nil }

        if #available(iOS 13, *) {
            let colour = [NSAttributedString.Key.foregroundColor: UIColor.label]
            string.addAttributes(colour, range: NSRange(location: 0, length: string.length))
        }

        self.init(attributedString: string)
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.