Using Dynamic Type With Web Views

Sometimes it is convenient to use a web view to show some static HTML content in an iOS app. If you have adopted dynamic type elsewhere in your app it can look odd if that text does not also respect the user’s choice of content size. Luckily there is a way to use dynamic type when displaying text in a web view.

Using Apple System Fonts

It turns out to be easy to use dynamic type with HTML content. You just need to choose the Apple system fonts when specifying the CSS font property. As long as you are on an Apple device you can use these fonts:

font: -apple-system-body 
font: -apple-system-headline 
font: -apple-system-subheadline 
font: -apple-system-caption1 
font: -apple-system-caption2 
font: -apple-system-footnote 
font: -apple-system-short-body 
font: -apple-system-short-headline 
font: -apple-system-short-subheadline 
font: -apple-system-short-caption1 
font: -apple-system-short-footnote 
font: -apple-system-tall-body 

For example, to have the HTML body default to the Apple system font body style:

body {
  font: -apple-system-body;
}

Note: If you are using the same content for non-Apple devices you should include a fallback font.

Example Using a WebKit View

A quick example to show how this works with a WKWebView. Here is a simple HTML document that I want to show in my iOS app.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="stylesheet.css" type="text/css" media="all" />
</head>
<body>
<h1>Dynamic Type With WebKit</h1>
<h2>Getting Started</h2>
<p>An example of using dynamic type fonts with HTML content displayed in a WKWebView.</p>
<p>Changing the text size in Settings should also change the text in this web view.</p>
<div class="footnote">For more details see <a href="https://useyourloaf.com/blog/using-dynamic-type-with-web-views/">Using Dynamic Type With Web Views</a></div>
</body>
</html>

This references a CSS stylesheet (stylesheet.css) that I include in the application bundle along with the HTML file to style the body, headings and footnote fonts:

body {
    font: -apple-system-body;
}

h1 {
    font: -apple-system-headline;
    color: red;
}

h2 {
    font: -apple-system-subheadline;
    color: blue;
}

.footnote {
    font: -apple-system-footnote;
    color: green;
}

Using A WKWebView

To show the HTML we can create a view controller with a basic WKWebView:

import UIKit
import WebKit

class HTMLViewControler: UIViewController {

  private lazy var webview: WKWebView = {
    let preferences = WKPreferences()
    preferences.javaScriptEnabled = false
    let configuration = WKWebViewConfiguration()
    configuration.preferences = preferences
    let webview = WKWebView(frame: .zero, configuration: configuration)
    webview.translatesAutoresizingMaskIntoConstraints = false
    return webview
  }()

Since I will only be showing static HTML content I have disabled JavaScript when creating the WKWebView. I pin the web view to the superview edges with some Auto Layout constraints:

  private func setupViews() {
    view.addSubview(webview)
    NSLayoutConstraint.activate([
        webview.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        webview.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        webview.topAnchor.constraint(equalTo: view.topAnchor),
        webview.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
  }

A helper method takes care of loading the HTML from a file in the application bundle into the web view:

  private func loadHTML(_ file: String) {
    if let baseURL = Bundle.main.resourceURL {
      let fileURL = baseURL.appendingPathComponent(file)
      webview.loadFileURL(fileURL, allowingReadAccessTo: fileURL)
    }
  }

Finally we set everything up from viewDidLoad:

  override func viewDidLoad() {
    super.viewDidLoad()
    setupViews()
    loadHTML("readme.html")
  }

Responding To Changes

Unfortunately there is no adjustsFontForContentSizeCategory property on WKWebView to automatically update fonts when the content size changes. Instead we need to listen for the notification and reload the view. Add the following line to viewDidLoad to observe the UIContentSizeCategoryDidChange notification:

NotificationCenter.default.addObserver(self, 
  selector: #selector(contentSizeDidChange(_:)),
  name: NSNotification.Name.UIContentSizeCategoryDidChange,
  object: nil)

The target method reloads the web view:

@objc private func contentSizeDidChange(_ notification: Notification) {
    webview.reload()
}

Here is how the view looks on an iPhone X at the default content size:

Default content size

Here is how it looks after changing the content size to the largest accessibility size:

Accessibility content size

Get The Code

You can get the full Xcode project for this post from my GitHub repository:

Further Reading

Never miss a post!

iOS Size Classes Cheat Sheet

Subscribe and get my free iOS Size Classes Cheat Sheet

Success! Now check your email to confirm your subscription and download your free guide to iOS Size Classes.

There was an error submitting your subscription. Please try again.

Unsubscribe at any time.
No time to watch WWDC videos?

Sign up to get my iOS posts direct to your inbox and I will send you a free PDF of my iOS Size Classes Cheat Sheet.

OK! Check your inbox (or spam folder) for an email to confirm your details and download your free guide to iOS Size Classes.

There was an error submitting your subscription. Please try again.

Unsubscribe at any time.
Archives Categories