iOS
UITextView
HTML Rendering
Swift
App Development

Display html text in uitextview

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

UITextView does not render HTML directly, but UIKit can turn HTML into an attributed string with reasonable fidelity. That makes it useful for showing formatted article bodies, email previews, or legal copy without embedding a full browser.

Convert HTML Into NSAttributedString

The core approach is to take an HTML string, encode it as Data, and initialize NSAttributedString with the .html document type. The result can be assigned to textView.attributedText.

swift
1import UIKit
2
3extension NSAttributedString {
4    static func fromHTML(_ html: String) throws -> NSAttributedString {
5        let data = Data(html.utf8)
6        return try NSAttributedString(
7            data: data,
8            options: [
9                .documentType: NSAttributedString.DocumentType.html,
10                .characterEncoding: String.Encoding.utf8.rawValue
11            ],
12            documentAttributes: nil
13        )
14    }
15}

That conversion is the important step. UITextView itself is only the display surface.

A Practical UIViewController Example

A small controller shows the usual setup. The example enables tapping links, applies a fallback font, and handles malformed HTML without crashing the screen.

swift
1import UIKit
2
3final class ArticleViewController: UIViewController, UITextViewDelegate {
4    private let textView = UITextView()
5
6    override func viewDidLoad() {
7        super.viewDidLoad()
8        view.backgroundColor = .systemBackground
9
10        textView.translatesAutoresizingMaskIntoConstraints = false
11        textView.isEditable = false
12        textView.isScrollEnabled = true
13        textView.dataDetectorTypes = [.link]
14        textView.delegate = self
15        view.addSubview(textView)
16
17        NSLayoutConstraint.activate([
18            textView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 16),
19            textView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -16),
20            textView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 16),
21            textView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16)
22        ])
23
24        let html = """
25        <html>
26          <head>
27            <style>
28              body { font-family: -apple-system; font-size: 17px; color: #222222; }
29              a { color: #0A84FF; }
30            </style>
31          </head>
32          <body>
33            <h2>Release Notes</h2>
34            <p>This screen renders <strong>HTML</strong> inside a <em>UITextView</em>.</p>
35            <p>Read more at <a href=\"https://developer.apple.com\">Apple Developer</a>.</p>
36          </body>
37        </html>
38        """
39
40        do {
41            textView.attributedText = try NSAttributedString.fromHTML(html)
42        } catch {
43            textView.text = "Failed to render content."
44        }
45    }
46
47    func textView(
48        _ textView: UITextView,
49        shouldInteractWith url: URL,
50        in characterRange: NSRange,
51        interaction: UITextItemInteraction
52    ) -> Bool {
53        print("Tapped link:", url.absoluteString)
54        return true
55    }
56}

This is enough for most app screens where the HTML is trusted and fairly simple.

Control Styling With Embedded CSS

HTML rendered through NSAttributedString supports a subset of CSS. Basic font, color, margin, emphasis, lists, and links usually work. Advanced layout, scripting, and many web features do not.

A useful pattern is to wrap incoming content in your own HTML template so the app controls typography consistently. That avoids depending on whatever inline styling a backend happens to send.

If the incoming content is only a fragment, such as a single paragraph, you can still wrap it in a small html and body document before parsing. This makes font handling more predictable.

Know When To Use WKWebView Instead

UITextView is good for rich text, not for full web content. If you need JavaScript, complex CSS layout, embedded media, or exact browser rendering, use WKWebView instead.

A good rule is simple:

  • use UITextView for formatted text with links
  • use WKWebView for document-like or browser-like experiences

That distinction matters for performance and for maintenance. A text view is lighter and easier to integrate with the rest of a native layout.

Handling Dynamic Content Safely

If the HTML comes from an API or user input, sanitize it before rendering. Even though NSAttributedString is not a browser, malformed or unexpectedly large HTML can still cause layout issues or poor performance.

It is also worth doing the conversion off the main thread when the content is large. Create the attributed string in a background task, then assign it back on the main queue.

swift
1let html = "<p>Hello, world.</p>"
2
3DispatchQueue.global(qos: .userInitiated).async {
4    let rendered = try? NSAttributedString.fromHTML(html)
5    DispatchQueue.main.async {
6        self.textView.attributedText = rendered
7    }
8}

Common Pitfalls

The most common problem is assuming every HTML feature will render. UITextView is not a browser, so layout differences are normal.

Another frequent issue is broken character encoding. If the server response is not UTF-8, text can display with replacement characters or fail to parse. Confirm the actual encoding before creating Data.

Developers also run into font inconsistency because the incoming HTML defines its own styles. Wrap the fragment in CSS you control instead of trying to patch the attributed string afterward.

Finally, avoid enabling editing unless the screen truly needs it. Editing can interfere with link behavior and change text container layout in ways that are surprising.

Summary

  • Render HTML in UITextView by converting it to NSAttributedString.
  • Use embedded CSS to keep typography and link colors consistent.
  • Prefer UITextView for rich text and WKWebView for full browser behavior.
  • Sanitize or validate dynamic HTML before rendering it.
  • Handle encoding and parsing errors explicitly so the UI fails gracefully.

Course illustration
Course illustration

All Rights Reserved.