Rendu HTML dans un Text()

Bonjour à tous,

Je me mets doucement à SwiftUI et je butte sur un point.
Je récupére des données distantes en JSON que je stocke correctement.

Cependant, lors de l’affichage, une des données est de l’HTML et je ne trouve pas de solution pour l’interpréter et l’afficher correctement (autre que d’invoquer une Webview …).

En Swift, j’utilisais cette extension :

extension String {
    var htmlToAttributedString: NSAttributedString? {
        guard let data = data(using: .utf8) else { return NSAttributedString() }
        do {
            return try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue], documentAttributes: nil)
        } catch {
            return NSAttributedString()
        }
    }

    var htmlToString: String {
        return htmlToAttributedString?.string ?? ""
    }
}

Auriez-vous une piste ?
Merci :slight_smile:

Hello @yann.gallis,

Ayant déjà joué un peu avec de l’HTML en Swift dans certains de mes projets, si je peux te donner un conseil, essaye d’éviter le rendu de l’HTML dans une de tes apps, ça n’apporte (presque) que des ennuis…

Sinon, tu as essayé: https://medium.com/@prafullkumar77/how-to-use-attributed-string-in-swiftui-21a17fbd5a64 ?

Bonne soirée,

Alexandre

Bonjour @Alexandre et merci pour ta réponse.

Evidemment j’aimerai aussi ne pas traiter d’HTML dans mon app mais ici, le but est d’afficher les actualités d’un site internet qui sont remplies dans un WYSIWYG. Donc il y a un peu de mise en forme, des liens etc…

Merci pour ta solution, c’est un bon début mais le test n’est pas concluant à 100%.
En effet, le Text() ou plutôt le HTMLText() ne s’affiche plus sur plusieurs lignes mais sur une seule et tronque mon texte.

J’ai aussi du adapter le code fourni dans medium car il n’était pas conforme au protocol UIViewRepresentable.

Voici le nouveau code :

import SwiftUI

struct HTMLText: UIViewRepresentable {
    func updateUIView(_ uiView: UILabel, context: Context) {}

    let html: String

    func makeUIView(context: UIViewRepresentableContext<Self>) -> UILabel {
        let label = UILabel()
        DispatchQueue.main.async {
            let data = Data(self.html.utf8)
            if let attributedString = try? NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) {
                label.attributedText = attributedString
            }
        }

        return label
    }
}

Si tu as une idée de pourquoi il ne se wrap pas, je suis preneur.
J’ai cherché un peu et j’avais trouvé le modifieur suivant, mais sans effet.

.fixedSize(horizontal: false, vertical: true)

Bon, c’est probablement car le rendu se fait dans un UILabel, je n’avais pas regardé le code plus que ça.

Je continue mes tests cet aprem et tient le topic à jour :wink:

1 « J'aime »

Je reviens à la charge :slight_smile:

J’ai pu modifier un peu le code et avoir un rendu « satisfaisant » mais que le code est moche et il reste un gros problème…

Voici le code modifié :

struct HTMLText: UIViewRepresentable {
func updateUIView(_ uiView: UILabel, context: Context) {}

let html: String
var width: CGFloat

func makeUIView(context: UIViewRepresentableContext<Self>) -> UILabel {
    let label = UILabel()
    DispatchQueue.main.async {
        let allHTML = getAllHTML(html: html)
        let data = Data(allHTML.utf8)
        if let attributedString = try? NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html], documentAttributes: nil) {
            label.attributedText = attributedString
            label.numberOfLines = 0
            label.translatesAutoresizingMaskIntoConstraints = false
            label.lineBreakMode = .byWordWrapping
            label.preferredMaxLayoutWidth = width
        }
    }

    return label
}

func getAllHTML(html: String) -> String {
    let header = """
    <html><head><style>
    * {
        font-family: sans-serif;
        font-size: 16px;
        text-align: justify;
    }
    p, h1, h2 {
        padding: 10px 25px 10px 25px;
    }
    a {
        color: #000000;
    }
    </style>
    """
    
    let footer = """
    </body></html>
    """
    
    return "\(header)\(html)\(footer)"
}

}

Donc oui, c’est moche.
Et le problème c’est que les liens présents dans mon texte ne sont pas cliquables…

Je vais continuer de chercher, mais si vous avez une piste, je suis preneur :wink: