Loading HTML text into UITextView
Using NSAttributedString. With pitfalls to avoid.
Published: Sept. 19, 2021 Sponsored App StoreRecently 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)
formatted.addAttributes([
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.