HTMLをNSAttributedTextに解析する-フォントの設定方法は?


133

iPhoneでUITableViewCellにうまく表示されるようにHTMLでフォーマットされたテキストのスニペットを取得しようとしています。

これまでのところ私はこれを持っています:

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

この種の作品。「Nice」が太字になっているテキストが表示されます。しかし...それはまた、フォントをTimes Romanに設定します!これは私が欲しい書体ではありません。documentAttributesに何かを設定する必要があると思っていますが、どこにも例が見つかりません。


1
注:NSHTMLTextDocumentTypeは遅くなる可能性があります。stackoverflow.com/questions/21166752/…を
finneycanhelp

重要:カスタムフォントを使用している場合は、この答えを確認する必要がありますstackoverflow.com/a/60786178/1223897
Yuvrajsinh

回答:


118

Javier Querolの回答に基づくSwift 2バージョン

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および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および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
現在のフォントを変更しないで、これは私が探していたものです、ありがとうございます!
Mohammad Zaid Pathan

2
これは機能します。変更された文字列をすぐに文字列に設定し、NSStringの初期化を省略できます。つまり、「<span style = \ "font-family:(self.font!.fontName); font-size:(self.font!.pointSize)\ ">(text)</ span>"
Matthew Korporaal

2
これを機能させるには(本当にうまく機能します)、font-familyの値を一重引用符で囲む必要があったため、<div style = \ "font-family: '(self.font!.fontName)'; ....
geraldcor

4
私は、iOS9から使用するのが最善だと思いますfont-family: '-apple-system', 'HelveticaNeue';(これは機能し、下位互換性もあります)。iOS9のみをサポートしている場合font-family: -apple-system;、使用できます
Daniel

1
また、テキストの色を設定する機能も便利です。16進文字列形式の値でスタイル属性に色を追加するだけですcolor: #000000。UIColorを16進文字列に変換するには、次のリンクを参照してください: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

この方法では、必要なフォントを指定する必要がなく、ラベルのフォントとサイズが使用されます。


2
これはとてもエレガントです!
Merlevede

2
いいね。NSAttributedString imoのカテゴリとしてはより理にかなっていますが。
Dimitris

@Javier Querolでは、リンクのクリック音をどのように処理しますか?
KarenAnne

を使用して文字列をデータにエンコードしNSUnicodeStringEncoding、次にデータを文字にエンコードして戻しNSUTF8StringEncodingます。大丈夫ですか?
Timur Bernikovich 2017

1
申し訳ありません-このソリューションは私にはうまくいきません、フォントが希望のフォントに設定されていません。-self.font.fontNameを使用する代わりに、self.font.familyNameを使用すると、目的のフォントが設定されますが、HTMLタグは保持されません。機能し、あらゆる種類のHTMLスタイルの使用に依存しない以下のソリューションを参照してください。-rrh
リッチー・ハイアット

49

私は実際にこの問題の実用的な解決策を見つけました:

解析される前にHTML応答文字列のフォントを変更する。

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

例:

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

Swiftバージョン:

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

4
最も簡単な解決法..他の答えは正解するかもしれませんが、困難な方法で物事を行うのは賢明ではありません.. :)
Sameera Chathuranga '10

2
ベストでスマートな答え
Tariq

最も賢い答え、同意!乾杯
ジムティアニー

こんにちは、実際にはこれはうまく機能しますが、この属性付きテキストをhtmlに戻すと、そのhtmlでフォントサイズが大きくなります
Mehul Thakkar

1
実際、stackoverflowを介した他の投稿の助けから..私は属性付きのテキストをhtmlに変換でき、フォントサイズを除いてすべてが正常に機能します。これにより、ほぼ2倍になります
Mehul Thakkar

41

理解した。クマのビット、そしておそらく最良の答えではありません。

このコードは、すべてのフォントの変更を通過します。フォントに「Times New Roman」と「Times New Roman BoldMT」を使用していることは知っています。しかし、とにかく、これは太字のフォントを見つけてリセットさせます。また、サイズをリセットすることもできます。

正直に言うと、解析時にこれを設定する方法があると思いますが、ある場合は見つかりません。

    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
フォント名に「BOLD」という単語を探すのは恐ろしいことです。これはまた、イタリックなどの他のフォント属性を壊します。
HughHughTeotl 2015年

1
より一般的なアプローチは、列挙しながらフォントの特性を確認し、同じ特性を持つフォントを作成することです。以下に私のコードを掲載します。
markiv 2015年

33

より一般的なアプローチは、列挙しながらフォントの特性を調べ、同じ特性(太字、斜体など)でフォントを作成することです。

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

これはあまり簡潔ではありませんが、より多くのhtmlでhtmlをラップする問題をハッキングするよりも安定しているようです。
syvex

23

はい、より簡単な解決策があります。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];

お役に立てれば。


23

UILabel拡張機能のSwift 4以降の更新

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

上記の回答は、の作成と同時に変換を実行している場合は問題ありませんNSAttributedString。しかし、文字列自体で機能するため、入力にアクセスする必要がないより良い解決策は、次のカテゴリです。

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

使用:

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

iOS 7以降で動作します


これをどこでも探しました... !! ありがとう。
Irshad Qureshi

5

色を含むビクターのソリューションの改善:

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

これを機能させるには、uicolorの16進数変換へのYLColor.swiftも必要になりますhttps://gist.github.com/yannickl/16f0ed38f0698d9a8ae7


4

NSHTMLTextDocumentTypeの使用は遅く、スタイルの制御が困難です。私はAtributikaと呼ばれる私のライブラリを試すことをお勧めします。独自の非常に高速なパーサーを備えています。また、タグ名やスタイルを定義することもできます。

例:

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

label.attributedText = str

ここで見つけることができますhttps://github.com/psharanda/Atributika


4

みんなの答えを組み合わせて、htmlテキストでラベルを設定できるようにする2つの拡張機能を作成しました。上記の回答の一部は、属性付き文字列のフォントファミリーを正しく解釈しませんでした。他のものは私のニーズに対して不完全だったか、他の方法で失敗しました。改善してほしい点がありましたらお知らせください。

これが誰かの役に立つことを願っています。

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

UILabelおよびで機能する唯一の完全なソリューションUITextView。ありがとう!
Radu Ursache

3

回答をありがとう、私は本当に拡張機能が好きでしたが、まだSwiftに変換していません。まだObjective-Cにいるこれらの古い学校の生徒にとって、これは少し役立つはずです: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];

}];    } 

ハッピーコーディング!-グレッグフレーム


1
古い学校の人たちに神に感謝します!:-)
Josef Rysanek 2017年

1

nilフォントを含むSwift 3文字列拡張。フォントなしのプロパティは、他のSOの質問から取得されます。どれを覚えていないか:(

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

Objective-Cを使用してNSAttributedStringを返すNSStringの拡張を次に示します。

HTMLタグを含む文字列を正しく処理し、BOLD、ITALICSなどのHTMLタグを保持しながら、目的のフォントとフォントの色を設定します...

何よりも、フォント属性の設定はHTMLマーカーに依存しません。

@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

実際には、さらに簡単でクリーンな方法が存在します。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)];

14
これは機能しますが、太字および斜体の<b>と<u>はフォントによって上書きされるため、失われます。
Zystem氏、2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.