Loading HTML text into UITextView

Using NSAttributedString. With pitfalls to avoid.

Published: Sept. 19, 2021
App Store

Recently I needed to display translated text that contained HTML links (the <a> tags) in UIKit. And it was quite complex process, which is the reason for this post.

While you can create an instance of NSAttributedString from string containing HTML tags, this in my case didn't preserve the font and color options set on UITextView.

So in the end I needed to create NSMutableAttributedString from the first NSAttributedString which let me add attributes like font , color and text alignment.

Loading HTML

Here is the final code:

let htmlString = "Click <a href='https://indiecatalog.app'>this</a> link!"

if let htmlData = htmlString.data(using: .utf8) {
    if let attributedString = try? NSAttributedString(
        data: htmlData,
        options: [.documentType: NSAttributedString.DocumentType.html,
                  .characterEncoding: String.Encoding.utf8.rawValue],
        documentAttributes: nil) {

        let paragraph = NSMutableParagraphStyle()
        paragraph.alignment = .center

        let formatted = NSMutableAttributedString(attributedString: attributedString)
            NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16),
            NSAttributedString.Key.foregroundColor: UIColor.secondaryLabel,
            NSAttributedString.Key.paragraphStyle: paragraph
        ], range: NSRange.init(location: 0, length: attributedString.length))

        textView.attributedText = formatted

You first need to create Data form your string and then use this data to init the first NSAttributedString. Specifying the UFT-8 encoding is super important, otherwise you might have an issues with certain characters.

In my case the loaded HTML was black, with strange font and size. So I needed to add the NSAttributedString.Key attributes to fix the font and color. There is also center alignment as a bonus via the NSMutableParagraphStyle.

Fortunately the links get color from the tint of UITextView so that was one less attribute to worry about.

When you want to apply attributes to entire NSAttributedString you can create NSRange like this:

NSRange.init(location: 0, length: attributedString.length)

⚠️ It is very important to use the length of the attributed string and not your original string. Because String and NSAttributedString count certain characters differently.

Opening URL in-app

When user taps URL in UITextView it gets opened in default browser by default. If you want to keep the user in your app, you need to utilize UITextViewDelegate and specify what happens when URL is tapped.

It's this method:

func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
    // open URL in SFSafariViewController for example        
    return false

You can also decide which URLs should be opened externally by returning true if you don't want to handle particular URL.

If you know better way to load HTML into UITextView, please let me know.

Filip Němeček profile photo


Filip Němeček @nemecek_f@iosdev.space

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀

iOS blogger and developer with interest in Python/Django. Want to see most recent projects? 👀