Search code examples
htmlswiftxcodeswiftuiwebkit

Parsing HTML and then styling it with attributedString, how do I handle superscripts?


My API is giving me back some information for a description that is HTML. When I give this as data to an attributed string, it shows text that is some default tiny times new roman and does not match the styling of the app. I decided to use attributed string to style it to use the default system font and a specific font size. However this changes the size of all the text, including the superscript "sup" elements that were in the HTML, resulting in some ugly text (line spacing is off). How do I handle setting the font size for only the regular text? Or another way, how do I go back and specifically make each superscript element a smaller font size?

Below is the component for handling the HTML:

struct HTMLText: UIViewRepresentable {
var html: String
var width: CGFloat

func makeUIView(context: UIViewRepresentableContext<Self>) -> UILabel {
    let label = UILabel()
    label.lineBreakMode = .byWordWrapping
    label.numberOfLines = 0
    label.preferredMaxLayoutWidth = width
    DispatchQueue.main.async {
        label.attributedText = formatAttributedString()
    }

    return label
}

func updateUIView(_ uiView: UILabel, context: Context) {
    DispatchQueue.main.async {
        uiView.attributedText = formatAttributedString()
    }
}

func formatAttributedString() -> NSMutableAttributedString? {
    let data = Data(self.html.utf8)
    guard let attributedString = try? NSMutableAttributedString(
        data: data,
        options: [
            .documentType: NSAttributedString.DocumentType.html,
            .characterEncoding: String.Encoding.utf8.rawValue
        ],
        documentAttributes: nil
    ) else {
        return nil
    }
    
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.minimumLineHeight = 20
    paragraphStyle.maximumLineHeight = 20
    let attributes = [
        NSAttributedString.Key.font: UIFont.systemFont(ofSize: 15),
        NSAttributedString.Key.paragraphStyle: paragraphStyle
    ]
    attributedString.addAttributes(attributes, range: NSRange(location: 0, length: attributedString.length))
    
    // possible solution to preserve sub/superscript styling
    // add a key right behind each superscript
    // then do a replace to remove it and store its position.
    // then add an attribute to make it a superscript at each position
    // For loop to go over attributed string and specifically set a smaller font size for superscript text and do it all in one go?
    
    return attributedString
    //return NSMutableAttributedString(string: attributedString.string, attributes: attributes)
}

I am HOPING that I don't have to do this manually with a loop or something and that I'm missing some easy property or method for this. Also ideally, the solution should not introduce any dependency on other packages just for parsing this HTML.


Solution

  • Don't use a UILabel, use Text's AttributedString constructor. See: How to use Attributed String in SwiftUI