Parsen von HTML in NSAttributedText - wie wird die Schriftart festgelegt?


132

Ich versuche, einen Textausschnitt zu erhalten, der in HTML formatiert ist, um in einer UITableViewCell auf einem iPhone gut angezeigt zu werden.

Bisher habe ich Folgendes:

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

Diese Art von Arbeiten. Ich bekomme einen Text mit der Aufschrift "Nice" in Fettdruck! Aber ... es setzt auch die Schriftart auf Times Roman! Dies ist nicht die Schriftart, die ich möchte. Ich denke, ich muss etwas in den documentAttributes festlegen, aber ich kann nirgendwo Beispiele finden.


1
Hinweis: NSHTMLTextDocumentType kann möglicherweise langsam sein. Siehe stackoverflow.com/questions/21166752/…
finneycanhelp

WICHTIG: Wenn Sie eine benutzerdefinierte Schriftart verwenden, muss diese Antwort angezeigt werden. Stackoverflow.com/a/60786178/1223897
Yuvrajsinh

Antworten:


117

Swift 2- Version, basierend auf der Antwort von 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 und 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 und 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
Ohne die aktuellen Schriftarten zu ändern, habe ich danach gesucht, danke Mann!
Mohammad Zaid Pathan

2
Das funktioniert. Sie können die geänderte Zeichenfolge sofort auf eine Zeichenfolge setzen und die NSString-Initialisierung weglassen, dh "<span style = \" font-family: (self.font! .FontName); Schriftgröße: (self.font! .pointSize) \ "> (Text) </ span>"
Matthew Korporaal

2
Damit dies funktioniert (was wirklich sehr gut funktioniert), musste ich einfache Anführungszeichen um den Wert der Schriftfamilie setzen, also <div style = \ "font-family: '(self.font! .FontName)'; ....
Geraldcor

4
Ich denke, seit iOS9 ist es am besten zu verwenden font-family: '-apple-system', 'HelveticaNeue';(was funktioniert und auch abwärtskompatibel ist). Wenn Sie nur iOS9 unterstützen, font-family: -apple-system;kann verwendet werden
Daniel

1
Praktisch ist auch die Möglichkeit, die Textfarbe festzulegen color: #000000. Fügen Sie dem Stilattribut einfach Farbe mit einem Wert im Hex-Zeichenfolgenformat hinzu . Siehe diesen Link zum Konvertieren von UIColor in Hex-Zeichenfolge: gist.github.com/yannickl/16f0ed38f0698d9a8ae7
Miroslav Hrivik

114
#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

Auf diese Weise müssen Sie nicht angeben, welche Schriftart Sie möchten, sondern die Schriftart und -größe des Etiketts.


2
Das ist sehr elegant!
Merlevede

2
Nett. Obwohl es als Kategorie auf NSAttributedString imo sinnvoller ist.
Dimitris

@ Javier Querol Wie gehe ich dann mit Link-Clinks um?
KarenAnne

Sie codieren Zeichenfolgen in Daten mit NSUnicodeStringEncodingund codieren Daten dann zurück in Zeichen mit NSUTF8StringEncoding. Ist es o.k?
Timur Bernikovich

1
Entschuldigung - diese Lösung funktioniert bei mir NICHT, die Schriftart ist nicht auf die gewünschte Schriftart eingestellt. - Anstatt self.font.fontName und stattdessen self.font.familyName zu verwenden, wird die gewünschte Schriftart festgelegt, die HTML-Tags bleiben jedoch nicht erhalten. Siehe Lösung unten, die funktioniert und nicht auf der Verwendung von HTML-Stilen jeglicher Art beruht. -rrh
Richie Hyatt

49

Ich habe tatsächlich eine funktionierende Lösung für dieses Problem gefunden:

Ändern Sie die Schriftart in Ihrer HTML-Antwortzeichenfolge, bevor sie analysiert wird.

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

Beispiel:

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

Schnelle Version:

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

4
Einfachste Lösung .. Andere Antworten können korrigieren, aber die Dinge schwieriger zu machen ist nicht klug .. :)
Sameera Chathuranga

2
Beste und kluge Antwort
Tariq

Klügste Antwort, einverstanden! Prost
Jim Tierney

Hallo, eigentlich funktioniert das großartig, aber wenn ich diesen zugeschriebenen Text zurück in HTML konvertiere, wird die Schriftgröße in diesem HTML erhöht
Mehul Thakkar

1
Eigentlich von Hilfe von anderen Posts über Stackoverflow. Ich bin in der Lage, attriubutierten Text in HTML zu konvertieren und alles funktioniert gut, abgesehen von der Schriftgröße, die fast verdoppelt wird
Mehul Thakkar

41

Herausgefunden. Ein bisschen wie ein Bär und vielleicht nicht die beste Antwort.

Dieser Code durchläuft alle Änderungen an der Schriftart. Ich weiß, dass für die Schriftarten "Times New Roman" und "Times New Roman BoldMT" verwendet werden. Trotzdem werden die fett gedruckten Schriftarten gefunden und ich kann sie zurücksetzen. Ich kann die Größe auch zurücksetzen, während ich dabei bin.

Ich hoffe / denke ehrlich, dass es einen Weg gibt, dies zur Analysezeit einzurichten, aber ich kann es nicht finden, wenn es einen gibt.

    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
Das Wort "BOLD" im Schriftnamen zu suchen, ist ein schrecklicher Kludge! Dadurch werden auch andere Schriftattribute wie Kursivschrift unterbrochen.
HughHughTeotl

1
Ein allgemeinerer Ansatz besteht darin, die Schriftmerkmale beim Aufzählen zu betrachten und eine Schriftart mit denselben Merkmalen zu erstellen. Ich werde meinen Code unten posten.
Markiv

33

Ein allgemeinerer Ansatz besteht darin, die Schriftmerkmale beim Aufzählen zu betrachten und eine Schriftart mit denselben Merkmalen (fett, kursiv usw.) zu erstellen:

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

Auch wenn dies nicht sehr prägnant ist, scheint es stabiler zu sein, als das Problem mit dem Umschließen von HTML mit mehr HTML zu umgehen.
Syvex

23

Ja, es gibt eine einfachere Lösung. Stellen Sie die Schriftart in der HTML-Quelle ein!

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

Hoffe das hilft.


23

Swift 4+ Update der UILabel- Erweiterung

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

Die Antworten funktionieren vor allem in Ordnung, wenn Sie die Konvertierung gleichzeitig mit dem Erstellen der durchführen NSAttributedString. Aber ich denke, eine bessere Lösung, die an der Zeichenfolge selbst funktioniert und daher keinen Zugriff auf die Eingabe benötigt, ist die folgende Kategorie:

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

Benutzen als:

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

Funktioniert unter iOS 7+


Überall danach gesucht ... !! Vielen Dank..!
Irshad Qureshi

5

Verbesserung von Victors Lösung, einschließlich Farbe:

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

Damit dies funktioniert, benötigen Sie auch YLColor.swift der Umwandlung von Uicolor in Hex https://gist.github.com/yannickl/16f0ed38f0698d9a8ae7


4

Die Verwendung von NSHTMLTextDocumentType ist langsam und schwer zu steuernde Stile. Ich empfehle Ihnen, meine Bibliothek mit dem Namen Atributika auszuprobieren. Es hat einen eigenen sehr schnellen Parser. Sie können auch beliebige Tag-Namen haben und einen beliebigen Stil für sie definieren.

Beispiel:

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

label.attributedText = str

Sie finden es hier https://github.com/psharanda/Atributika


4

Ich habe alle Antworten zusammengefügt und zwei Erweiterungen erstellt, mit denen eine Beschriftung mit HTML-Text festgelegt werden kann. Einige der obigen Antworten haben die Schriftfamilie in den zugewiesenen Zeichenfolgen nicht richtig interpretiert. Andere waren für meine Bedürfnisse unvollständig oder scheiterten auf andere Weise. Lassen Sie mich wissen, ob ich mich verbessern soll.

Ich hoffe das hilft jemandem.

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

die einzige Komplettlösung, die für UILabelund funktioniert UITextView. Vielen Dank!
Radu Ursache

3

Vielen Dank für die Antworten, die Erweiterung hat mir sehr gut gefallen, aber ich habe noch nicht auf Swift umgestellt. Für die alten Schüler, die sich noch in Ziel C befinden, sollte dies ein wenig helfen: 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];

}];    } 

Viel Spaß beim Codieren! - Greg Frame


1
Gott sei Dank für alte Schüler! :-)
Josef Rysanek

1

Swift 3 String-Erweiterung mit einer Null-Schriftart. Die Eigenschaft ohne Schriftart stammt aus einer anderen SO-Frage. Erinnern Sie sich nicht an welche :(

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

Hier ist eine Erweiterung für NSString, die mit Objective-C einen NSAttributedString zurückgibt.

Es behandelt eine Zeichenfolge mit HTML-Tags korrekt und legt die gewünschte Schriftart und Schriftfarbe fest, wobei HTML-Tags wie BOLD, ITALICS ... beibehalten werden.

Das Beste ist, dass zum Festlegen der Schriftattribute keine HTML-Markierungen erforderlich sind.

@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

Tatsächlich gibt es einen noch einfacheren und saubereren Weg. Legen Sie einfach die Schriftart fest, nachdem Sie den HTML-Code analysiert haben:

 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
Das funktioniert, aber Sie verlieren fett und kursiv <b> und <u>, weil diese von der Schriftart überschrieben werden.
Herr Zystem
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.