web-dev-qa-db-ja.com

Swift-独自のスタイルを持つHTML文字列のフォントを変更する

私は動的にWordpress APIからHTML文字列を取得し、それを属性付き文字列に解析してアプリに表示します。文字列には独自のスタイルがあるため、さまざまなフォントとサイズが表示されます。それがデザインの選択に影響を与えています。

私がしたいのは、属性付き文字列全体のフォントとそのサイズを変更することです。

私は属性付き文字列のオプションでそうしようとしましたが、何もしません:

let attributedT = try! NSAttributedString(
            data: nContent!.decodeHTML().data(using: String.Encoding.unicode, allowLossyConversion: true)!,
            options: [ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType, NSFontAttributeName: UIFont(name: "Helvetica", size: 16.0)!],
            documentAttributes: nil)
        contentLbl.attributedText = attributedT

これを達成する方法について誰かが何かアイデアを持っていますか?

追伸文字列の最初または最後にCSSタグを追加できることはわかっていますが、これにより、他のスタイルがオーバーライドされますか?また、これが有効な解決策である場合、それを行う方法のサンプルを提供していただけませんか?

11
Jacobo Koenig

SetAttributesは、HTMLからすべての属性をリセットします。これを回避するための拡張メソッドを作成しました。

Swift 4

public convenience init?(HTMLString html: String, font: UIFont? = nil) throws {
    let options : [NSAttributedString.DocumentReadingOptionKey : Any] =
        [NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html,
         NSAttributedString.DocumentReadingOptionKey.characterEncoding: String.Encoding.utf8.rawValue]

    guard let data = html.data(using: .utf8, allowLossyConversion: true) else {
        throw NSError(domain: "Parse Error", code: 0, userInfo: nil)
    }

    if let font = font {
        guard let attr = try? NSMutableAttributedString(data: data, options: options, documentAttributes: nil) else {
            throw NSError(domain: "Parse Error", code: 0, userInfo: nil)
        }
        var attrs = attr.attributes(at: 0, effectiveRange: nil)
        attrs[NSAttributedStringKey.font] = font
        attr.setAttributes(attrs, range: NSRange(location: 0, length: attr.length))
        self.init(attributedString: attr)
    } else {
        try? self.init(data: data, options: options, documentAttributes: nil)
    }
}

試験サンプル:

let html = "<html><body><h1 style=\"color:red;\">html text here</h1></body></html>"
let font = UIFont.systemFont(ofSize: 16)

var attr = try NSMutableAttributedString(HTMLString: html, font: nil)
var attrs = attr?.attributes(at: 0, effectiveRange: nil)
attrs?[NSAttributedStringKey.font] as? UIFont
// print: <UICTFont: 0x7ff19fd0a530> font-family: "TimesNewRomanPS-BoldMT"; font-weight: bold; font-style: normal; font-size: 24.00pt

attr = try NSMutableAttributedString(HTMLString: html, font: font)
attrs = attr?.attributes(at: 0, effectiveRange: nil)
attrs?[NSAttributedStringKey.font] as? UIFont
// print: <UICTFont: 0x7f8c0cc04620> font-family: ".SFUIText"; font-weight: normal; font-style: normal; font-size: 16.00pt
11
bubuxu

基本的には、NSAttributedStringをNSMutableAttributedStringに変換します。

let attributedT = // ... attributed string
let mutableT = NSMutableAttributedString(attributedString:attributedT)

これで addAttributes を呼び出して、異なるフォントなどの属性を、全体などの任意の範囲に適用できます。

ただし、残念ながら、イタリックなどの記号的特徴がないフォントは、その記号的特徴を持つフォントと異なるフォントです。したがって、既存のシンボリック特性をフォントからコピーして別のフォントに適用するユーティリティが必要になります。

func applyTraitsFromFont(_ f1: UIFont, to f2: UIFont) -> UIFont? {
    let t = f1.fontDescriptor.symbolicTraits
    if let fd = f2.fontDescriptor.withSymbolicTraits(t) {
        return UIFont.init(descriptor: fd, size: 0)
    }
    return nil
}

さて、そのユーティリティで武装して、試してみましょう。単純なHTMLから始め、それを属性付き文字列に変換します。

let html = "<p>Hello <i>world</i>, hello</p>"
let data = html.data(using: .utf8)!
let att = try! NSAttributedString.init(
    data: data, options: [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType],
    documentAttributes: nil)
let matt = NSMutableAttributedString(attributedString:att)

ご覧のとおり、私がアドバイスしたように、NSMutableAttributedStringに変換しました。次に、フォントの観点からスタイルの実行を循環させ、ユーティリティを使用して既存の特性を適用しながら別のフォントに変更します。

matt.enumerateAttribute(
    NSFontAttributeName,
    in:NSMakeRange(0,matt.length),
    options:.longestEffectiveRangeNotRequired) { value, range, stop in
        let f1 = value as! UIFont
        let f2 = UIFont(name:"Georgia", size:20)!
        if let f3 = applyTraitsFromFont(f1, to:f2) {
            matt.addAttribute(
                NSFontAttributeName, value:f3, range:range)
        }
    }

結果は次のとおりです。

enter image description here

デザインのニーズに応じて、この手順をさらに洗練されたものにすることができるのは明らかです。

17
matt

Swift 4ソリューション


  • NSAttributedString拡張と便利な初期化子
  • 属性付き文字列(HTMLドキュメント)フォント属性を列挙し、提供されたUIFontで置き換えます
  • 元のHTMLフォントサイズを保持するか、提供されたUIFont、@ see useDocumentFontSizeパラメータのfont-sizeを使用します
  • このメソッドは、フォントを操作するオーバーロードなしで、HTMLをNSAttributedStringに単純に変換できます。フォントパラメータをスキップするだけです。@ see guardステートメント

extension NSAttributedString {

    convenience init(htmlString html: String, font: UIFont? = nil, useDocumentFontSize: Bool = true) throws {
        let options: [NSAttributedString.DocumentReadingOptionKey : Any] = [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
        ]

        let data = html.data(using: .utf8, allowLossyConversion: true)
        guard (data != nil), let fontFamily = font?.familyName, let attr = try? NSMutableAttributedString(data: data!, options: options, documentAttributes: nil) else {
            try self.init(data: data ?? Data(html.utf8), options: options, documentAttributes: nil)
            return
        }

        let fontSize: CGFloat? = useDocumentFontSize ? nil : font!.pointSize
        let range = NSRange(location: 0, length: attr.length)
        attr.enumerateAttribute(.font, in: range, options: .longestEffectiveRangeNotRequired) { attrib, range, _ in
            if let htmlFont = attrib as? UIFont {
                let traits = htmlFont.fontDescriptor.symbolicTraits
                var descrip = htmlFont.fontDescriptor.withFamily(fontFamily)

                if (traits.rawValue & UIFontDescriptorSymbolicTraits.traitBold.rawValue) != 0 {
                    descrip = descrip.withSymbolicTraits(.traitBold)!
                }

                if (traits.rawValue & UIFontDescriptorSymbolicTraits.traitItalic.rawValue) != 0 {
                    descrip = descrip.withSymbolicTraits(.traitItalic)!
                }

                attr.addAttribute(.font, value: UIFont(descriptor: descrip, size: fontSize ?? htmlFont.pointSize), range: range)
            }
        }

        self.init(attributedString: attr)
    }

}

使用方法1(フォントの置き換え)

let attr = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!", font: UIFont.systemFont(ofSize: 34, weight: .thin))

使用法2(NSMutableAttributedStringの例)

let attr = try! NSMutableAttributedString(htmlString: "<strong>Hello</strong> World!", font: UIFont.systemFont(ofSize: 34, weight: .thin))
attr.append(NSAttributedString(string: " MINIMIZE", attributes: [.link: "@m"]))

使用法3(HTMLをNSAttributedStringに変換するだけ)

let attr = try? NSAttributedString(htmlString: "<strong>Hello</strong> World!")
13
AamirR

Swift以前の(Swift 4)ソリューションのバージョン


extension NSAttributedString {

    convenience init(htmlString html: String, font: UIFont? = nil, useDocumentFontSize: Bool = true) throws {
        let options: [String : Any] = [
            NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
            NSCharacterEncodingDocumentAttribute: String.Encoding.utf8.rawValue
        ]

        let data = html.data(using: .utf8, allowLossyConversion: true)
        guard (data != nil), let fontFamily = font?.familyName, let attr = try? NSMutableAttributedString(data: data!, options: options, documentAttributes: nil) else {
            try self.init(data: data ?? Data(html.utf8), options: options, documentAttributes: nil)
            return
        }

        let fontSize: CGFloat? = useDocumentFontSize ? nil : font!.pointSize
        let range = NSRange(location: 0, length: attr.length)
        attr.enumerateAttribute(NSFontAttributeName, in: range, options: .longestEffectiveRangeNotRequired) { attrib, range, _ in
            if let htmlFont = attrib as? UIFont {
                let traits = htmlFont.fontDescriptor.symbolicTraits
                var descrip = htmlFont.fontDescriptor.withFamily(fontFamily)

                if (traits.rawValue & UIFontDescriptorSymbolicTraits.traitBold.rawValue) != 0 {
                    descrip = descrip.withSymbolicTraits(.traitBold)!
                }

                if (traits.rawValue & UIFontDescriptorSymbolicTraits.traitItalic.rawValue) != 0 {
                    descrip = descrip.withSymbolicTraits(.traitItalic)!
                }

                attr.addAttribute(NSFontAttributeName, value: UIFont(descriptor: descrip, size: fontSize ?? htmlFont.pointSize), range: range)
            }
        }

        self.init(attributedString: attr)
    }

}
5
AamirR
let font = "<font face='Montserrat-Regular' size='13' color= 'black'>%@"
let html = String(format: font, yourhtmlstring)
webView.loadHTMLString(html, baseURL: nil)
0
urvashi bhagat