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.

Last updated: Jun 12, 2020

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>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<link rel="stylesheet" href="stylesheet.css">
<title>User Guide</title>
</head>
<body>
<h1>Dynamic Type With WebKit</h1>
<h2>Getting Started</h2>
<p>An example of using <mark>dynamic type</mark> fonts with HTML content displayed in a WKWebView. Changing the text size in Settings should also change the text in this web view.</p>
<footer>For more details see <a href="https://useyourloaf.com/blog/using-dynamic-type-with-web-views/">Using Dynamic Type With Web Views</a></footer>
</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 footer:

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

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

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

footer {
    font: -apple-system-footnote;
}

Using A WKWebView

To show the HTML I’m created a view controller with a WKWebView. I have disabled JavaScript but otherwise I’m using a default configuration when creating the WKWebView.

import UIKit
import WebKit

final class WebViewControler: UIViewController {
  private lazy var webView: WKWebView = {
    let preferences = WKPreferences()
    preferences.javaScriptEnabled = false
    let configuration = WKWebViewConfiguration()
    configuration.preferences = preferences
    return WKWebView(frame: .zero, configuration: configuration)
  }()

I load the HTML from the main bundle, using “default.html” as the default:

  var html: String = "default" {
    didSet {
      loadHTML(html)
    }
  }

  private func loadHTML(_ name: String) {
    if let url = Bundle.main.url(forResource: name, withExtension: "html") {
      webView.loadFileURL(url, allowingReadAccessTo: url)
    }
  }

To keep it simple I make the web view the root view of the view controller in loadView:

override func loadView() {
  view = webView
  loadHTML(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 loadView or viewDidLoad to observe the UIContentSizeCategory notification:

NotificationCenter.default.addObserver(self, 
  selector: #selector(contentSizeDidChange(_:)),
  name: UIContentSizeCategory.didChangeNotification,
  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 8 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

Supporting Dark Mode

I’ve also updated the project to support dark mode. This requires iOS 13. See the following post for details:

Get The Code

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

Further Reading