Parsing HTML ke dalam NSAttributedText - bagaimana cara mengatur font?

133

Saya mencoba mendapatkan potongan teks yang diformat dalam html untuk ditampilkan dengan baik di iPhone di UITableViewCell.

Sejauh ini saya punya ini:

NSError* error;
NSString* source = @"<strong>Nice</strong> try, Phil";
NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithData:[source dataUsingEncoding:NSUTF8StringEncoding]
                                                           options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                                                     NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]}
                                                              documentAttributes:nil error:&error];

Jenis pekerjaan ini. Saya mendapatkan beberapa teks dengan tulisan 'Nice' dalam huruf tebal! Tapi ... itu juga mengatur font menjadi Times Roman! Ini bukan wajah font yang saya inginkan. Saya pikir saya perlu mengatur sesuatu di documentAttributes, tetapi, saya tidak dapat menemukan contoh di mana pun.

phil
sumber
1
Catatan: NSHTMLTextDocumentType berpotensi lambat. Lihat stackoverflow.com/questions/21166752/…
finneycanhelp
PENTING: Jika Anda menggunakan font khusus, Anda perlu melihat jawaban ini stackoverflow.com/a/60786178/1223897
Yuvrajsinh

Jawaban:

118

Versi Swift 2 , berdasarkan jawaban yang diberikan oleh Javier Querol

extension UILabel {
    func setHTMLFromString(text: String) {
        let modifiedFont = NSString(format:"<span style=\"font-family: \(self.font!.fontName); font-size: \(self.font!.pointSize)\">%@</span>", text) as String

        let attrStr = try! NSAttributedString(
            data: modifiedFont.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)!,
            options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding],
            documentAttributes: nil)

        self.attributedText = attrStr
    }
}

Swift 3.0 dan iOS 9+

extension UILabel {
    func setHTMLFromString(htmlText: String) {
        let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%@</span>", htmlText)

        let attrStr = try! NSAttributedString(
            data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
            options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue],
            documentAttributes: nil)

        self.attributedText = attrStr
    }
}

Swift 5 dan iOS 11+

extension UILabel {
    func setHTMLFromString(htmlText: String) {
        let modifiedFont = String(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%@</span>", htmlText)

        let attrStr = try! NSAttributedString(
            data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
            options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue],
            documentAttributes: nil)

        self.attributedText = attrStr
    }
}
Víctor Albertos
sumber
1
Tidak mengubah font saat ini, ini yang saya cari, terima kasih ya!
Mohammad Zaid Pathan
2
Ini bekerja. Anda dapat mengatur string yang dimodifikasi menjadi sebuah String segera dan menghilangkan inisialisasi NSString yaitu "<span style = \" font-family: (self.font! .FontName); font-size: (self.font! .pointSize) \ "> (text) </span>"
Matthew Korporaal
2
Untuk membuat ini bekerja, (yang bekerja sangat sangat baik) saya harus menambahkan tanda kutip tunggal di sekitar nilai font-family sehingga <div style = \ "font-family: '(self.font! .FontName)'; ....
geraldcor
4
Saya pikir, sejak iOS9, yang terbaik adalah menggunakan font-family: '-apple-system', 'HelveticaNeue';(yang berfungsi, dan juga kompatibel dengan mundur). Jika Anda hanya mendukung iOS9 font-family: -apple-system;dapat digunakan
Daniel
1
Juga berguna adalah kemampuan untuk mengatur warna teks, cukup tambahkan warna ke atribut style dengan nilai dalam format hex string color: #000000. Lihat tautan ini untuk mengonversi UIColor ke hex string: gist.github.com/yannickl/16f0ed38f0698d9a8ae7
Miroslav Hrivik
115
#import "UILabel+HTML.h"

@implementation UILabel (HTML)

- (void)jaq_setHTMLFromString:(NSString *)string {

    string = [string stringByAppendingString:[NSString stringWithFormat:@"<style>body{font-family: '%@'; font-size:%fpx;}</style>",
                                              self.font.fontName,
                                              self.font.pointSize]];
    self.attributedText = [[NSAttributedString alloc] initWithData:[string dataUsingEncoding:NSUnicodeStringEncoding]
                                                           options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                                                     NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}
                                                documentAttributes:nil
                                                             error:nil];
}


@end

Dengan cara ini Anda tidak perlu menentukan font mana yang Anda inginkan, itu akan mengambil font dan ukuran label.

Javier Querol
sumber
2
Ini sangat elegan!
Merlevede
2
Bagus. Meskipun lebih masuk akal sebagai kategori pada NSAttributedString imo.
Dimitris
@Javier Querol Lalu bagaimana cara menangani denting tautan?
KarenAnne
Anda menyandikan string ke data dengan NSUnicodeStringEncodingdan kemudian menyandikan data kembali ke karakter dengan NSUTF8StringEncoding. Apakah itu oke?
Timur Bernikovich
1
maaf - solusi ini TIDAK berfungsi untuk saya, font tidak diatur ke font yang diinginkan. - Alih-alih menggunakan self.font.fontName dan alih-alih menggunakan self.font.familyName tidak mengatur font yang diinginkan tetapi kemudian tag HTML tidak dipertahankan. lihat solusi di bawah ini yang berfungsi dan tidak mengandalkan penggunaan gaya HTML apa pun. -rrh
Richie Hyatt
49

Saya sebenarnya menemukan solusi untuk masalah ini:

Mengubah font di string respons HTML Anda sebelum diuraikan.

NSString *aux = [NSString stringWithFormat:@"<span style=\"font-family: YOUR_FONT_NAME; font-size: SIZE\">%@</span>", htmlResponse];

Contoh:

NSString *aux = [NSString stringWithFormat:@"<span style=\"font-family: HelveticaNeue-Thin; font-size: 17\">%@</span>", [response objectForKey:@"content"]];

Versi cepat:

let aux = "<span style=\"font-family: YOUR_FONT_NAME; font-size: SIZE\">\(htmlResponse)</span>"
Teodor Ciuraru
sumber
4
Solusi termudah .. Jawaban lain mungkin benar tetapi melakukan hal-hal dengan cara yang lebih sulit tidak cerdas .. :)
Sameera Chathuranga
2
Jawaban terbaik dan cerdas
Tariq
Jawaban cerdas, disetujui! Cheers
Jim Tierney
halo, Sebenarnya ini bekerja dengan baik, tetapi jika saya mengonversi teks yang dikaitkan ini kembali ke html, ukuran font semakin meningkat dalam html itu
Mehul Thakkar
1
Sebenarnya dari bantuan dari posting lain melalui stackoverflow .. saya dapat mengkonversi teks yang dirubah ke html dan semuanya bekerja dengan baik terlepas dari ukuran font, yang menjadi hampir dua kali lipat
Mehul Thakkar
41

Menemukannya. Sedikit beruang, dan mungkin bukan jawaban terbaik.

Kode ini akan melalui semua perubahan font. Saya tahu bahwa itu menggunakan "Times New Roman" dan "Times New Roman BoldMT" untuk font. Tapi bagaimanapun, ini akan menemukan huruf tebal dan biarkan saya mengatur ulang Saya juga dapat mengatur ulang ukuran saat saya melakukannya.

Sejujurnya saya berharap / berpikir ada cara untuk mengatur ini pada waktu parse, tetapi saya tidak dapat menemukannya jika ada.

    NSRange range = (NSRange){0,[str length]};
    [str enumerateAttribute:NSFontAttributeName inRange:range options:NSAttributedStringEnumerationLongestEffectiveRangeNotRequired usingBlock:^(id value, NSRange range, BOOL *stop) {
        UIFont* currentFont = value;
        UIFont *replacementFont = nil;

        if ([currentFont.fontName rangeOfString:@"bold" options:NSCaseInsensitiveSearch].location != NSNotFound) {
            replacementFont = [UIFont fontWithName:@"HelveticaNeue-CondensedBold" size:25.0f];
        } else {
            replacementFont = [UIFont fontWithName:@"HelveticaNeue-Thin" size:25.0f];
        }

        [str addAttribute:NSFontAttributeName value:replacementFont range:range];
    }];
phil
sumber
2
Mencari kata "BOLD" dalam nama font adalah kludge yang mengerikan! Ini juga merusak atribut font lain seperti huruf miring.
HughHughTeotl
1
Pendekatan yang lebih umum adalah dengan melihat ciri font saat enumerasi, dan membuat font dengan sifat yang sama. Saya akan memposting kode saya di bawah ini.
markiv
33

Pendekatan yang lebih umum adalah dengan melihat sifat font saat menghitung, dan membuat font dengan sifat yang sama (cetak tebal, miring, dll.):

extension NSMutableAttributedString {

    /// Replaces the base font (typically Times) with the given font, while preserving traits like bold and italic
    func setBaseFont(baseFont: UIFont, preserveFontSizes: Bool = false) {
        let baseDescriptor = baseFont.fontDescriptor
        let wholeRange = NSRange(location: 0, length: length)
        beginEditing()
        enumerateAttribute(.font, in: wholeRange, options: []) { object, range, _ in
            guard let font = object as? UIFont else { return }
            // Instantiate a font with our base font's family, but with the current range's traits
            let traits = font.fontDescriptor.symbolicTraits
            guard let descriptor = baseDescriptor.withSymbolicTraits(traits) else { return }
            let newSize = preserveFontSizes ? descriptor.pointSize : baseDescriptor.pointSize
            let newFont = UIFont(descriptor: descriptor, size: newSize)
            self.removeAttribute(.font, range: range)
            self.addAttribute(.font, value: newFont, range: range)
        }
        endEditing()
    }
}
markiv
sumber
Meskipun ini tidak terlalu ringkas, tampaknya lebih stabil daripada meretas masalah dengan membungkus html dengan lebih banyak html.
syvex
23

Ya, ada solusi yang lebih mudah. Atur font di sumber html!

NSError* error;
NSString* source = @"<strong>Nice</strong> try, Phil";
source = [source stringByAppendingString:@"<style>strong{font-family: 'Avenir-Roman';font-size: 14px;}</style>"];
NSMutableAttributedString* str = [[NSMutableAttributedString alloc] initWithData:[source dataUsingEncoding:NSUTF8StringEncoding]
                                                           options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                                                     NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]}
                                                              documentAttributes:nil error:&error];

Semoga ini membantu.

Maks
sumber
23

Swift 4+ pembaruan ekstensi UILabel

extension UILabel {
    func setHTMLFromString(text: String) {
        let modifiedFont = NSString(format:"<span style=\"font-family: \(self.font!.fontName); font-size: \(self.font!.pointSize)\">%@</span>" as NSString, text)

        let attrStr = try! NSAttributedString(
            data: modifiedFont.data(using: String.Encoding.unicode.rawValue, allowLossyConversion: true)!,
            options: [NSAttributedString.DocumentReadingOptionKey.documentType:NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue],
            documentAttributes: nil)

        self.attributedText = attrStr
    }
}

iOS 9+

extension UILabel {
    func setHTMLFromString(htmlText: String) {
        let modifiedFont = NSString(format:"<span style=\"font-family: '-apple-system', 'HelveticaNeue'; font-size: \(self.font!.pointSize)\">%@</span>" as NSString, htmlText) as String


        //process collection values
        let attrStr = try! NSAttributedString(
            data: modifiedFont.data(using: .unicode, allowLossyConversion: true)!,
            options: [NSAttributedString.DocumentReadingOptionKey.documentType:NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue],
            documentAttributes: nil)


        self.attributedText = attrStr
    }
}
Rafat touqir Rafsun
sumber
8

Jawaban di atas semuanya berfungsi OK jika Anda melakukan konversi bersamaan dengan membuat NSAttributedString. Tapi saya pikir solusi yang lebih baik, yang bekerja pada string itu sendiri dan karena itu tidak memerlukan akses ke input, adalah kategori berikut:

extension NSMutableAttributedString
{
    func convertFontTo(font: UIFont)
    {
        var range = NSMakeRange(0, 0)

        while (NSMaxRange(range) < length)
        {
            let attributes = attributesAtIndex(NSMaxRange(range), effectiveRange: &range)
            if let oldFont = attributes[NSFontAttributeName]
            {
                let newFont = UIFont(descriptor: font.fontDescriptor().fontDescriptorWithSymbolicTraits(oldFont.fontDescriptor().symbolicTraits), size: font.pointSize)
                addAttribute(NSFontAttributeName, value: newFont, range: range)
            }
        }
    }
}

Digunakan sebagai:

let desc = NSMutableAttributedString(attributedString: *someNSAttributedString*)
desc.convertFontTo(UIFont.systemFontOfSize(16))

Bekerja di iOS 7+

HughHughTeotl
sumber
Mencari kemana-mana untuk ini ... !! Terima kasih..!
Irshad Qureshi
5

Memperbaiki solusi Victor, termasuk warna:

extension UILabel {
      func setHTMLFromString(text: String) {
          let modifiedFont = NSString(format:"<span style=\"color:\(self.textColor.toHexString());font-family: \(self.font!.fontName); font-size: \(self.font!.pointSize)\">%@</span>", text) as String

          let attrStr = try! NSAttributedString(
              data: modifiedFont.dataUsingEncoding(NSUnicodeStringEncoding, allowLossyConversion: true)!,
              options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: NSUTF8StringEncoding],
              documentAttributes: nil)

          self.attributedText = attrStr
      }
  }

Agar ini berfungsi, Anda juga perlu YLColor. Perpindahan dari konversi uicolor ke hex https://gist.github.com/yannickl/16f0ed38f0698d9a8ae7

Juan Carlos Ospina Gonzalez
sumber
4

Penggunaan NSHTMLTextDocumentType lambat dan sulit untuk mengontrol gaya. Saya sarankan Anda untuk mencoba perpustakaan saya yang disebut Atributika. Ini memiliki pengurai yang sangat cepat. Anda juga dapat memiliki nama tag dan menentukan gaya apa pun untuknya.

Contoh:

let str = "<strong>Nice</strong> try, Phil".style(tags:
    Style("strong").font(.boldSystemFont(ofSize: 15))).attributedString

label.attributedText = str

Anda dapat menemukannya di sini https://github.com/psharanda/Atributika

Pavel Sharanda
sumber
4

Bergabung bersama jawaban semua orang, saya membuat dua ekstensi yang memungkinkan pengaturan label dengan teks html. Beberapa jawaban di atas tidak benar menafsirkan keluarga font di string yang dikaitkan. Yang lain tidak lengkap untuk kebutuhan saya atau gagal dengan cara lain. Beri tahu saya jika ada yang ingin Anda tingkatkan.

Saya harap ini membantu seseorang.

extension UILabel {
    /// Sets the label using the supplied html, using the label's font and font size as a basis.
    /// For predictable results, using only simple html without style sheets.
    /// See /programming/19921972/parsing-html-into-nsattributedtext-how-to-set-font
    ///
    /// - Returns: Whether the text could be converted.
    @discardableResult func setAttributedText(fromHtml html: String) -> Bool {
        guard let data = html.data(using: .utf8, allowLossyConversion: true) else {
            print(">>> Could not create UTF8 formatted data from \(html)")
            return false
        }

        do {
            let mutableText = try NSMutableAttributedString(
                data: data,
                options: [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html, NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue],
                documentAttributes: nil)
            mutableText.replaceFonts(with: font)
            self.attributedText = mutableText
            return true
        } catch (let error) {
            print(">>> Could not create attributed text from \(html)\nError: \(error)")
            return false
        }
    }
}

extension NSMutableAttributedString {

    /// Replace any font with the specified font (including its pointSize) while still keeping
    /// all other attributes like bold, italics, spacing, etc.
    /// See /programming/19921972/parsing-html-into-nsattributedtext-how-to-set-font
    func replaceFonts(with font: UIFont) {
        let baseFontDescriptor = font.fontDescriptor
        var changes = [NSRange: UIFont]()
        enumerateAttribute(.font, in: NSMakeRange(0, length), options: []) { foundFont, range, _ in
            if let htmlTraits = (foundFont as? UIFont)?.fontDescriptor.symbolicTraits,
                let adjustedDescriptor = baseFontDescriptor.withSymbolicTraits(htmlTraits) {
                let newFont = UIFont(descriptor: adjustedDescriptor, size: font.pointSize)
                changes[range] = newFont
            }
        }
        changes.forEach { range, newFont in
            removeAttribute(.font, range: range)
            addAttribute(.font, value: newFont, range: range)
        }
    }
}
dwsolberg
sumber
satu-satunya solusi lengkap yang berfungsi untuk UILabeldan UITextView. Terima kasih!
Radu Ursache
3

Terima kasih atas jawabannya, saya sangat menyukai ekstensi tetapi saya belum mengonversi ke swift. Bagi anak-anak sekolah tua yang masih di Objective-C ini seharusnya sedikit membantu: D

-(void) setBaseFont:(UIFont*)font preserveSize:(BOOL) bPreserve {

UIFontDescriptor *baseDescriptor = font.fontDescriptor;

[self enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, [self length]) options:0 usingBlock:^(id  _Nullable value, NSRange range, BOOL * _Nonnull stop) {

    UIFont *font = (UIFont*)value;
    UIFontDescriptorSymbolicTraits traits = font.fontDescriptor.symbolicTraits;
    UIFontDescriptor *descriptor = [baseDescriptor fontDescriptorWithSymbolicTraits:traits];
    UIFont *newFont = [UIFont fontWithDescriptor:descriptor size:bPreserve?baseDescriptor.pointSize:descriptor.pointSize];

    [self removeAttribute:NSFontAttributeName range:range];
    [self addAttribute:NSFontAttributeName value:newFont range:range];

}];    } 

Selamat Coding! --Greg Frame

Greg Frame
sumber
1
Terima kasih Tuhan untuk anak sekolah tua! :-)
Josef Rysanek
1

Ekstensi Swift 3 String termasuk font nil. Properti tanpa font diambil dari pertanyaan SO lainnya, jangan ingat yang mana :(

extension String {
    var html2AttributedString: NSAttributedString? {
        guard let data = data(using: .utf8) else {
            return nil
        }

        do {
            return try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)
        }
        catch {
            print(error.localizedDescription)
            return nil
        }
    }

    public func getHtml2AttributedString(font: UIFont?) -> NSAttributedString? {
        guard let font = font else {
            return html2AttributedString
        }

        let modifiedString = "<style>body{font-family: '\(font.fontName)'; font-size:\(font.pointSize)px;}</style>\(self)";

        guard let data = modifiedString.data(using: .utf8) else {
            return nil
        }

        do {
            return try NSAttributedString(data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue], documentAttributes: nil)
        }
        catch {
            print(error)
            return nil
        }
    }
}
shelll
sumber
0

Berikut ini adalah ekstensi untuk NSString yang mengembalikan NSAttributedString menggunakan Objective-C.

Ini dengan benar menangani string dengan tag HTML dan menetapkan font dan warna font yang diinginkan sambil mempertahankan tag HTML termasuk BOLD, ITALICS ...

Yang terbaik dari semuanya itu tidak bergantung pada penanda HTML untuk mengatur atribut font.

@implementation NSString (AUIViewFactory)

- (NSAttributedString*)attributedStringFromHtmlUsingFont:(UIFont*)font fontColor:(UIColor*)fontColor
{
    NSMutableAttributedString* mutableAttributedString = [[[NSAttributedString alloc] initWithData:[self dataUsingEncoding:NSUTF8StringEncoding] options:@{NSDocumentTypeDocumentAttribute : NSHTMLTextDocumentType, NSCharacterEncodingDocumentAttribute : @(NSUTF8StringEncoding)} documentAttributes:nil error:nil] mutableCopy]; // parse text with html tags into a mutable attributed string
    [mutableAttributedString beginEditing];
    // html tags cause font ranges to be created, for example "This text is <b>bold</b> now." creates three font ranges: "This text is " , "bold" , " now."
    [mutableAttributedString enumerateAttribute:NSFontAttributeName inRange:NSMakeRange(0, mutableAttributedString.length) options:0 usingBlock:^(id value, NSRange range, BOOL* stop)
    { // iterate every font range, change every font to new font but preserve symbolic traits such as bold and italic (underline and strikethorugh are preserved automatically), set font color
        if (value)
        {
            UIFont* oldFont = (UIFont*)value;
            UIFontDescriptor* fontDescriptor = [font.fontDescriptor fontDescriptorWithSymbolicTraits:oldFont.fontDescriptor.symbolicTraits];
            UIFont* newFont = [UIFont fontWithDescriptor:fontDescriptor size:font.pointSize];
            [mutableAttributedString removeAttribute:NSFontAttributeName range:range]; // remove the old font attribute from this range
            [mutableAttributedString addAttribute:NSFontAttributeName value:newFont range:range]; // add the new font attribute to this range
            [mutableAttributedString addAttribute:NSForegroundColorAttributeName value:fontColor range:range]; // set the font color for this range
        }
    }];
    [mutableAttributedString endEditing];
    return mutableAttributedString;
}

@end
Richie Hyatt
sumber
-3

Sebenarnya, cara yang lebih mudah dan bersih ada. Cukup atur font setelah parsing HTML:

 NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithData:[htmlString dataUsingEncoding:NSUTF8StringEncoding]
                                                                     options:@{
                                                                               NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
                                                                               NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)}
                                                          documentAttributes:nil error:nil];
    [text addAttributes:@{NSFontAttributeName: [UIFont fontWithName:@"Lato-Regular" size:20]} range:NSMakeRange(0, text.length)];
Erik
sumber
14
Itu berfungsi, tetapi Anda akan kehilangan huruf tebal dan miring <b> dan <u> karena itu ditimpa oleh font.
Tn. Zystem