2

I have some localization strings like this: "Register and get <b>all</b>\n<b>rewards cards</b> of our partners\n<b>in one</b> universal <b>card</b>"

So parts of it should be bold, and also there is a requirement for paragraph style.

This code works perfect for styling except bold parts:

let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.lineSpacing = 1.0
        paragraphStyle.alignment = .center
        paragraphStyle.minimumLineHeight = 18.0

let attributedString = NSMutableAttributedString(string: descriptionString, attributes: [.font: UIFont.proximaNovaRegularWithSize(size: 15.0),
                                                                                                 .paragraphStyle: paragraphStyle])

Also I have perfect solution to parse html tags:

func convertFromHTMLString(_ input: String?) -> NSMutableAttributedString? {
        guard let input = input else { return nil }

        guard let data = input.data(using: String.Encoding.unicode, allowLossyConversion: true) else { return nil  }
        return try? NSMutableAttributedString(data: data, options: [NSAttributedString.DocumentReadingOptionKey.documentType : NSAttributedString.DocumentType.html], documentAttributes: nil)
    }

But it's not working combined. So my question - how to parse html tags and add attributes to it? And the solution should be totally dynamic, to be used for different localization.

Note: I can't use markdown text as app should be available for iOS lower than 15.0

After used proposed solution

let attr = try? NSMutableAttributedString(
            data: data ?? Data(),
            options: [
                .documentType: NSAttributedString.DocumentType.html,
                .characterEncoding: String.Encoding.utf8.rawValue,
                .defaultAttributes: [
                    NSAttributedString.Key.font: UIFont.proximaNovaRegularWithSize(size: 15.0),
                    NSAttributedString.Key.paragraphStyle: paragraphStyle
                ],
            ],
            documentAttributes: nil
        )

result is: enter image description here

still no font and styling

2 Answers 2

2

Try passing the attributes as defaultAttributes:

NSMutableAttributedString(
    data: data,
    options: [
        .documentType: NSAttributedString.DocumentType.html,
        .characterEncoding: String.Encoding.utf8.rawValue,
        .defaultAttributes: [
            NSAttributedString.Key.font: UIFont.proximaNovaRegularWithSize(size: 15.0),
            NSAttributedString.Key.paragraphStyle: paragraphStyle
        ],
    ],
    documentAttributes: nil
)
Sign up to request clarification or add additional context in comments.

4 Comments

I have error "Reference to member 'font' cannot be resolved without a contextual type" and the same for paragraphStyle
Edited to fully qualify NSAttributedString.Key values.
thank you, html tags parsed correctly, but no styling(fonts, style), please check image at post
You're right, that's sad. I see the paragraphStyle can be added using addAttributes(), but the adding the font removes the bold parts. Sorry, not sure how to solve that.
1

Ok, I got the solution, maybe it will help anyone else.

Note, that my strings are strictly multi lined, so it's easy first split them, then add needed font and size to the parts, and then add paragraph styling.

I've played with order of parsing tags/styling fonts/styling paragraph, and at every case something was missed. If you don't need to separate line as multiline in strict order, just don't do mapping. Otherwise, you can miss breaking while styling or parsing tags. enter image description here

 descriptionLabel.attributedText = getAttributedDescriptionText(for: "Register and get <b>all</b>\n<b>rewards cards</b> of our partners\n<b>in one</b> universal <b>card</b>", fontDescription: "ProximaNova-Regular", fontSize: 15)   



func getAttributedDescriptionText(for descriptionString: String, fontDescription: String, fontSize: Int) -> NSAttributedString? {
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.lineSpacing = 1.0
    paragraphStyle.alignment = .center
    paragraphStyle.minimumLineHeight = 18.0

    let attributedString = NSMutableAttributedString()
    let splits = descriptionString.components(separatedBy: "\n")
    _ = splits.map { string in
        let modifiedFont = String(format:"<span style=\"font-family: '\(fontDescription)'; font-size: \(fontSize)\">%@</span>", string)
        let data = modifiedFont.data(using: String.Encoding.unicode, allowLossyConversion: true)
        let attr = try? NSMutableAttributedString(
            data: data ?? Data(),
            options: [
                .documentType: NSAttributedString.DocumentType.html,
                .characterEncoding: String.Encoding.utf8.rawValue
            ],
            documentAttributes: nil
        )
        attributedString.append(attr ?? NSMutableAttributedString())
        if string != splits.last {
            attributedString.append(NSAttributedString(string: "\n"))
        }
    }
    attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length))
    return attributedString
} 

1 Comment

Don't use map() for that, use forEach instead. It's be easier to just add <span style=\"font-family: ...> & </span> only once, at start & end of the String. No need to loop.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.