HTML'yi NSAttributedText olarak ayrıştırma - yazı tipi nasıl ayarlanır?


133

Bir UITableViewCell'de bir iPhone'da güzel bir şekilde görüntülemek için html olarak biçimlendirilmiş bir metin parçası almaya çalışıyorum.

Şimdiye kadar şuna sahibim:

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];

Bu tür işler. Kalın yazılmış 'Güzel' metni alıyorum! Ama ... aynı zamanda yazı tipini Times Roman olarak belirler! İstediğim yazı tipi yüzü bu değil. DocumentAttributes içinde bir şey ayarlamam gerektiğini düşünüyorum, ancak hiçbir yerde örnek bulamıyorum.


1
Not: NSHTMLTextDocumentType potansiyel olarak yavaş olabilir. Stackoverflow.com/questions/21166752/… sayfasına
finneycanhelp

ÖNEMLİ: Özel yazı tipi kullanıyorsanız, bu cevabı görmeniz gerekir stackoverflow.com/a/60786178/1223897
Yuvrajsinh

Yanıtlar:


118

Javier Querol tarafından verilen cevaba göre Swift 2 versiyonu

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

1
Mevcut yazı tiplerini değiştirmiyorum, aradığım şey buydu, teşekkürler dostum!
Mohammad Zaid Pathan

2
Bu çalışıyor. Değiştirilen dizeyi hemen bir Dize olarak ayarlayabilir ve NSString başlatmayı atlayabilirsiniz, yani "<span style = \" font-family: (self.font! .FontName); font-size: (self.font! .pointSize) \ "> (metin) </span>"
Matthew Korporaal

2
Bunun çalışması için (gerçekten çok iyi çalışıyor) font-family değerinin etrafına tek tırnak işaretleri eklemem gerekiyordu, bu yüzden <div style = \ "font-family: '(self.font! .FontName)'; ....
geraldcor

4
Bence iOS9'dan beri kullanmak en iyisidir font-family: '-apple-system', 'HelveticaNeue';(çalışır ve aynı zamanda geriye dönük uyumludur). Yalnızca iOS9'u destekliyorsanız font-family: -apple-system;kullanılabilir
Daniel

1
Ayrıca metin rengini ayarlama yeteneği de kullanışlıdır, sadece onaltılık dize biçiminde değerle stil niteliğine renk ekleyin color: #000000. UIColor'u onaltılık dizeye dönüştürmek için bu bağlantıya bakın: 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

Bu şekilde, istediğiniz yazı tipini belirlemenize gerek kalmaz, etiket yazı tipi ve boyutunu alır.


2
Bu çok zarif!
Merlevede

2
Güzel. NSAttributedString imo üzerinde bir kategori olarak daha mantıklı olsa da.
Dimitris

@Javier Querol O zaman bağlantı tıklamaları nasıl işlenir?
KarenAnne

NSUnicodeStringEncodingDizeyi veriye kodlar ve ardından veriyi ile karakterlere geri kodlarsınız NSUTF8StringEncoding. Tamam mı?
Timur Bernikovich

1
üzgünüm - bu çözüm benim için ÇALIŞMIYOR, yazı tipi istenen yazı tipine ayarlanmadı. - self.font.fontName yerine self.font.familyName kullanmak yerine istenen yazı tipini belirler, ancak HTML etiketleri korunmaz. işe yarayan ve her türlü HTML stilini kullanmaya dayanmayan aşağıdaki çözüme bakın. -rrh
Richie Hyatt

49

Aslında bu soruna çalışan bir çözüm buldum:

Ayrıştırılmadan önce HTML yanıt dizenizdeki yazı tipini değiştirmek.

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

Misal:

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

Swift versiyonu:

let aux = "<span style=\"font-family: YOUR_FONT_NAME; font-size: SIZE\">\(htmlResponse)</span>"

4
En Kolay Çözüm .. Diğer cevaplar doğru olabilir ama işleri daha zor şekilde yapmak akıllıca değildir .. :)
Sameera Chathuranga

2
En iyi ve akıllı cevap
Tarık

En akıllı cevap, kabul etti! Şerefe
Jim Tierney

merhaba, Aslında bu harika çalışıyor, ancak bu atıfta bulunulan metni tekrar
html'ye dönüştürürsem

1
Aslında stackoverflow üzerinde diğer yayınlardan yardım dan .. ben html için attriubuted metni dönüştürmek mümkün ve her şey neredeyse iki katına oluyor yani, yazı tipi boyutu dışında çalışma para cezası
Mehul Thakkar

41

Anladım. Biraz ayıcık ve belki de en iyi cevap değil.

Bu kod, tüm yazı tipi değişikliklerinden geçecektir. Yazı tipleri için "Times New Roman" ve "Times New Roman BoldMT" kullandığını biliyorum. Ama ne olursa olsun, bu kalın yazı tiplerini bulacak ve onları sıfırlamama izin verecek. Ben de oradayken boyutu sıfırlayabilirim.

Bunu ayrıştırma zamanında kurmanın bir yolu olduğunu gerçekten umuyorum / düşünüyorum, ancak varsa bulamıyorum.

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

2
Yazı tipi adında "BOLD" kelimesini aramak korkunç bir karışıklıktır! Bu aynı zamanda italik gibi diğer yazı tipi niteliklerini de bozar.
HughHughTeotl

1
Daha genel bir yaklaşım, numaralandırırken yazı tipi özelliklerine bakmak ve aynı özelliklere sahip bir yazı tipi oluşturmaktır. Kodumu aşağıya göndereceğim.
markiv

33

Daha genel bir yaklaşım, numaralandırırken yazı tipi özelliklerine bakmak ve aynı özelliklere (kalın, italik, vb.) Sahip bir yazı tipi oluşturmaktır:

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

Bu çok özlü olmasa da, html'yi daha fazla html ile sarmalayarak sorunu çözmekten daha kararlı görünüyor.
syvex

23

Evet, daha kolay bir çözüm var. Yazı tipini html kaynağında ayarlayın!

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];

Bu yardımcı olur umarım.


23

UILabel uzantısının Swift 4+ güncellemesi

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

8

Her şeyden önce yanıtlar işe yarar, eğer dönüşümü NSAttributedString. Ancak, dizenin kendisinde çalışan ve bu nedenle girdiye erişime ihtiyaç duymayan daha iyi bir çözümün aşağıdaki kategori olduğunu düşünüyorum:

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

Kullanım şekli:

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

İOS 7+ üzerinde çalışır


Bunun için her yerde aradım ... !! Teşekkürler..!
Irshad Qureshi

5

Renk dahil olmak üzere Victor'un çözümünde iyileştirme:

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

Bunun çalışması için, uicolor'un onaltılık dönüşümüne ait YLColor.swift'e de ihtiyacınız olacak https://gist.github.com/yannickl/16f0ed38f0698d9a8ae7


4

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

Misal:

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

label.attributedText = str

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


4

Herkesin yanıtlarını bir araya getirerek html metni ile bir etiket belirlemeye izin veren iki uzantı yaptım. Yukarıdaki bazı yanıtlar, atfedilen dizelerdeki yazı tipi ailesini doğru şekilde yorumlamadı. Diğerleri ihtiyaçlarım için eksikti veya başka şekillerde başarısız oldu. Geliştirmemi istediğiniz herhangi bir şey varsa bana bildirin.

Umarım bu birine yardımcı olur.

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

için çalışan tek komple çözüm UILabelve UITextView. Teşekkürler!
Radu Ursache

3

Cevaplar için teşekkürler, uzantıyı gerçekten beğendim ama henüz hızlıya geçmedim. Hala Objective-C'de olan eski okul öğrencileri için bu biraz yardımcı olacaktır: 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];

}];    } 

Mutlu Kodlama! --Greg Çerçeve


1
Yaşlı okulcular için Tanrıya şükür! :-)
Josef Rysanek

1

Swift 3 Sıfır yazı tipi içeren dize uzantısı. Yazı tipsiz özellik diğer SO sorusundan alınmıştır, hangisi olduğunu hatırlamayın :(

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

0

Burada, Objective-C kullanarak bir NSAttributedString döndüren NSString için bir uzantı var.

BOLD, ITALICS ...

Hepsinden iyisi, yazı tipi niteliklerini ayarlamak için herhangi bir HTML işaretleyicisine güvenmemesidir.

@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

-3

Aslında daha da kolay ve temiz bir yol var. HTML'yi ayrıştırdıktan sonra yazı tipini ayarlamanız yeterlidir:

 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)];

14
Bu işe yarar, ancak kalın ve italik <b> ve <u> karakterlerini kaybedersiniz çünkü bunların üzerine yazı tipi yazılır.
Bay Zystem
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.