web-dev-qa-db-fra.com

Swift - Changer la police sur une chaîne HTML qui a ses propres styles

Je reçois de manière dynamique une chaîne HTML à partir d'une API Wordpress et je l'analyse en une chaîne attribuée pour l'afficher dans mon application. Comme la chaîne a ses propres styles, elle affiche différentes polices et tailles, quelque chose qui affecte nos choix de conception.

Ce que je veux faire, c'est changer la police et sa taille sur toute la chaîne attribuée.

J'ai essayé de le faire dans les options de la chaîne attribuée, mais cela ne fait rien:

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

Quelqu'un at-il des idées sur la façon d'y parvenir?

P.S. Je sais que je pourrais ajouter une balise CSS au début ou à la fin de la chaîne, mais cela remplacerait-il les autres styles? De plus, s'il s'agit d'une solution valide, pourriez-vous s'il vous plaît fournir un exemple sur la façon de le faire?

11
Jacobo Koenig

SetAttributes réinitialisera tous les attributs de HTML. J'ai écrit une méthode d'extension pour éviter cela:

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

Échantillon de test:

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

Ce que vous voulez faire, fondamentalement, c'est transformer la NSAttributedString en NSMutableAttributedString.

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

Vous pouvez maintenant appeler addAttributes pour appliquer des attributs, tels qu'une police différente, sur n'importe quelle plage souhaitée, comme le tout.

Malheureusement, cependant, une police sans trait symbolique tel que l'italique est une police différente d'une police avec ce trait symbolique. Par conséquent, vous allez avoir besoin d'un utilitaire qui copie les traits symboliques existants d'une police et les applique à une autre police:

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
}

D'accord, donc, armé de cet utilitaire, essayons-le. Je vais commencer avec du HTML simple et le convertir en une chaîne attribuée, comme vous le faites:

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)

Comme vous pouvez le voir, je l'ai converti en NSMutableAttributedString, comme je l'ai conseillé. Maintenant, je vais parcourir les styles en termes de police, en modifiant une police différente tout en utilisant mon utilitaire pour appliquer les traits existants:

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

Voici le résultat:

enter image description here

Évidemment, vous pouvez modifier cette procédure pour être encore plus sophistiqué, en fonction de vos besoins de conception.

17
matt

Swift 4 solution


  • NSAttributedString extension avec initialiseur pratique
  • Énumère les attributs de police de la chaîne attribuée (document HTML) et les remplace par les UIFont fournis
  • Préserve les tailles de police HTML d'origine ou utilise la taille de police du paramètre UIFont, @see useDocumentFontSize fourni
  • Cette méthode peut simplement convertir HTML en NSAttributedString, sans la surcharge de manipulation avec des polices, il suffit de sauter le paramètre de police, instruction @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)
    }

}

sage-1 (Remplacer la police)

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

sage-2 (NSMutableAttributedString exemple)

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

sage-3 (convertir uniquement HTML en NSAttributedString)

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

Swift version de ma précédente solution (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