iOSでHTMLをNSAttributedStringに変換する


151

私はのインスタンス使用していますUIWebViewいくつかのテキストを処理し、それを正しく色を、それをHTMLとして結果を与えるのではなく、それを表示するよりも、UIWebView私が使用してそれを表示したいCore TextNSAttributedString

作成して描画することはできNSAttributedStringますが、HTMLを属性付きの文字列に変換してマップする方法がわかりません。

Mac OS X NSAttributedStringにはinitWithHTML:メソッドがあることを理解していますが、これはMacのみの追加であり、iOSでは使用できません。

これと同様の質問があることも知っていますが、答えはありませんでしたが、もう一度やり直して、誰かがこれを行う方法を作成したかどうか、作成した場合は共有できるかどうかを確認します。


2
NSAttributedString-Additions-for-HTMLライブラリは名前が変更され、同じ作成者によってフレームワークに組み込まれました。現在はDTCoreTextと呼ばれ、多数のCore Textレイアウトクラスが含まれています。あなたはここで
ブライアン・ダグラス・モクリー

回答:


290

iOS 7では、UIKitはHTMLを使用してinitWithData:options:documentAttributes:error:初期化できるメソッドを追加しましたNSAttributedString。例:

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

Swiftの場合:

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
何らかの理由で、オプションNSDocumentTypeDocumentAttribute:NSHTMLTextDocumentTypeが原因で、エンコードに本当に長い時間がかかります:(
Arie Litovsky

14
NSRangeで属性を設定するよりも、NSHTMLTextDocumentTypeが(文字通り)〜1000倍遅くなります。(1つの太字のタグが付いた短いラベルのプロファイルが作成されました。)
Jason Moore

6
バックグラウンドスレッドから使​​用する場合は、このメソッドでNSHTMLTextDocumentTypeを使用できない場合に注意してください。iOS 7でも、HTMLレンダリングにTextKitを使用しません。Ingveが推奨するDTCoreTextライブラリをご覧ください。
TJez 2013年

2
驚くばかり。考えてみれば、おそらく[NSNumber numberWithInt:NSUTF8StringEncoding]を@(NSUTF8StringEncoding)として実行できますか?
Jarsen 2013

15
私はこれを行っていましたが、iOS 8では注意してください。数百文字の場合、1秒に近いほど遅くなります。(iOS 7では、それはほぼ瞬時でした。)
Norman

43

GithubのOliver DrobnikによるNSAttributedString への作業中のオープンソースの追加があります。HTML解析にはNSScannerを使用します。


iOSの4.3の分展開が必要です:(なし-レス、非常に印象的。
ああ、ダニー・ボーイ

3
@Lirik Overkillはあなたのためかもしれませんが、他の誰かにとっては完璧です。つまり、あなたのコメントは少しも役に立ちません。
wuf810 2014年

3
このプロジェクトに必要なのはオープンソースであり、標準の2条項BSDライセンスでカバーされていることに注意してください。つまり、このコードの元の作成者としてCocoaneticsに言及し、アプリ内でLICENSEテキストを複製する必要があります。
ダルガン2015

28

HTMLからNSAttributedStringを作成するには、メインスレッドで行う必要があります。

更新:NSAttributedString HTMLレンダリングは内部でWebKitに依存しており、メインスレッドで実行する必要があります。そうないと、アプリがSIGTRAPでクラッシュすることがあります

New Relicクラッシュログ:

ここに画像の説明を入力してください

以下は、更新されたスレッドセーフな Swift 2 String拡張です。

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

使用法:

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

出力:

ここに画像の説明を入力してください


アンドリュー。これは正常に動作しています。このアプローチを使用する場合、UITextViewで処理する必要があるすべての短いイベントを知りたいと思いました。HTMLで利用可能なカレンダーイベント、通話、メール、ウェブサイトリンクなどを処理できますか?UITextViewがUILabelと比較してイベントを処理できることを願っています。
harshit2811 2016

上記のアプローチはフォーマットにのみ適しています。イベント処理が必要な場合は、TTTAttributedLabelを使用することをお勧めします。
Andrew Schreiber

NSAttributedStringが使用するデフォルトのエンコーディングはNUTTF16StringEncodingです(UTF8ではありません!)。これが機能しない理由です。少なくとも私の場合は!
Umit Kaya 2017

これは受け入れられる解決策です。バックグラウンドスレッドでHTML文字列の会話を行うとしますテストを実行しながら、最終的にはかなり頻繁にクラッシュ、と。
ratimihah

21

NSAttributedStringのSwift初期化子拡張

私の傾向は、これをとNSAttributedStringいうよりも拡張として追加することでしたString。静的拡張と初期化子として試してみました。私は以下に含めたイニシャライザを好みます。

スウィフト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)
}

スウィフト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)
}
}

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

Hello Worldをこのようにしたい<p> <b> <i> hello </ i> </ b> <i> world </ i> </ p>
Uma Madhavi 2017

いくつかのLOC保存して置き換えるguard ... NSMutableAttributedString(data:...ことでtry self.init(data:...(と追加throwsのinitに)
nyg

そして最後にそれは動作しません-テキストはランダムなフォントサイズを取得します
Vyachaslav Gerchicov '13

2
UTF-8でデータをデコードしていますが、UTF-16でエンコードしています
Shyam Bhat

11

これは、StringHTML文字列をとして返すためにSwiftで記述された拡張機能です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
    }
}

使用するには、

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

上記では、意図的にユニコードを正しく表示することを示すために、意図的にユニコード\ u2022を追加しました。

些細なこと:をNSAttributedString使用するデフォルトのエンコーディングはNSUTF16StringEncoding(UTF8ではない!)です。


UTF16は私の日を救ってくれました。ありがとうsamwize!
Yueyu

UTF16は私の日を救ってくれました。ありがとうsamwize!
Yueyu

6

Andrewに変更を加えましたのソリューションに、コードをSwift 3に更新しました。

このコードは、UITextViewを使用しself、元のフォント、フォントサイズ、テキストの色を継承できるようになりました。

注:ここtoHexString()からの拡張です

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

使用例:

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

5

Swift 3.0 Xcode 8バージョン

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

スウィフト4


  • NSAttributedString簡易イニシャライザ
  • 余分なガードなし
  • エラーをスローします

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

}

使用法

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

あなたは私の日を救います。ありがとうございました。
pkc456

@ pkc456meta.stackexchange.com/questions/5234/… 、賛成投票をしてください:)ありがとうございます!
AamirR

フォントサイズとフォントファミリーを設定するにはどうすればよいですか?
kirqe

それはself.init(attributedString:attributedString)での冗長なコピーを含まないため、モバイルダンによって提案されたものよりもはるかに優れています
シアン化物

4

現在の唯一の解決策は、HTMLを解析し、指定されたpoint / font / etc属性を持ついくつかのノードを構築し、それらをNSAttributedStringに結合することです。これは大変な作業ですが、正しく行われれば、将来的に再利用可能になる可能性があります。


1
HTMLがXHTML-Strictの場合は、NSXMLDOcumentなどを使用して解析を支援できます。
Dylan Lukes 2010年

特定の属性を持つノードを構築することをどのように提案しますか?
ジョシュア

2
これは実装の詳細です。ただし、HTMLを解析すると、各タグの各属性にアクセスでき、フォント名やサイズなどを指定できます。この情報を使用して、属性付きテキストに属性として追加する必要がある関連詳細を保存できます。一般に、そのようなタスクに取り組む前に、まず構文解析に慣れる必要があります。
10

2

上記のソリューションは正しいです。

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

ただし、iOS 8.1、2、または3で実行している場合、アプリはクラッシュします。

クラッシュを回避するには、これをキューで実行します。常にメインスレッド上にあるようにします。


@alecex私は同じ問題に遭遇しました!アプリはiOS 8.1、2、3ではクラッシュしますが、iOS 8.4以降では問題ありません。それを避ける方法を詳細に説明できますか?または回避策はありますか、代わりにメソッドを使用できますか?
強い

私はこれを処理する簡単なカテゴリを作成し、AppKitからメソッドをコピーしました。これは、これを行うのが非常に簡単で直感的な方法です。Appleが追加しなかった理由は私を超えています。:github.com/cguess/NSMutableAttributedString-HTML
CGuess

2

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

例:

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

label.attributedText = str

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


2

Swift 3
これを試してください

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

そして使用するために:

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

役立つ拡張機能

、私は上の拡張書いたこのスレッド、ポッド、そしてエリカSadunのにObjCの例でiOSのグルメクックブックP.80にインスピレーションを受けString、オンをNSAttributedStringGitHubの上- HTMLのプレーン弦とNSAttributedStringsの間を行き来し、その逆はここで、その参考になりました。

署名は(GISTで再び、完全なコード、上記のリンク)です。

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

フォント付き

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

または、これが派生したバージョンを使用して、attributedStringを設定した後にUILabelにフォントを設定できます。


0

組み込みの変換では、.forgroundColorを別の値に設定して属性ディクショナリを渡した場合でも、テキストの色は常にUIColor.blackに設定されます。iOS 13でDARKモードをサポートするには、NSAttributedStringでこのバージョンの拡張機能を試してください。

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)
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.