Dynamic Type & In-App Font Scaling
Our guide to supporting custom fonts & accessibility
The Dynamic Type feature allows users to choose the size of textual content displayed on the screen. It helps users who need larger text for better readability. It also accommodates those who can read smaller text, allowing more information to appear on the screen. Apps that support Dynamic Type also provide a more consistent reading experience.
Apple's Developer Documentation
Since iOS 7, Apple has provided users with the ability to adjust the size of displayed content in your apps. Most content-driven apps support this feature seamlessly. This provides great support for every reader, but what if you want to provide extended scaling? What occurs behind the scenes, and what caveats (if any) are presented? You’re probably here because you need to support Dynamic Type or possibly build your own font scaling system, and we can help you.
First, let‘s take a look at the weight, size, and leading values for each text style in the default content category size (Large). The text style will determine the scale factor needed to support Dynamic Type.
Next, let‘s take a look at the Dynamic Type settings in iOS Settings, which can be found either under Accessibility or Display & Brightness. The “Larger Text” setting below is in Accessibility → Display & Text Size → Larger Text.
Setting up Dynamic Type
Luckily, in order to support Dynamic Type with system fonts, all we need are a few lines:
There‘s also an option to set the “automatically adjusts font” flag in Interface Builder.
In Interface Builder, the Dynamic Type option to automatically adjust fonts applies only to text styles or scaled fonts returned by UIFontMetrics. It has no effect on custom fonts set in Interface Builder.
Apple's Developer Documentation
Notice how we didn’t set a custom font on the label but instead relied on the system font. There isn‘t much that is required to support the built-in system scaling.
As we saw above in the default content category size, the text styles are scaled at different sizes. In some cases you may want to provide font sizes that aren‘t listed. How can we support Dynamic Type if we want to use a custom font and size in our app?
Custom Font Scaling with UIFontMetrics
So, how can we guarantee our font size will be met with the Dynamic Type requirements? In order to observe changes, we will need to subscribe to UIContentSizeCategory.didChangeNotification
.
This sounds simple enough, but what if we have multiple screens to observe? It doesn’t seem very optimal to register for the same notification on each view controller. If your app is primarily navigation controller-based, a way around this would be to subclass UINavigationController
. We would simply iterate through the array of child view controllers (and their children) to set the preferredContentSizeCategory
and override the trait collection to scale our custom font. Our notification to observe dynamic font size changes would look like this:
The key here is to override the view controller‘s trait collection with the correct UIContentSizeCategory
whether it‘s user selected or the current preferredContentSizeCategory
. That looks something like this:
All that‘s left is to apply the font as a type of UIFontMetrics
.
If you use a custom font in your app and want to let the user control the text size, you must create a scaled instance of the font in your source code. Call `scaledFont(for:)`, passing in a reference to the custom font that's at a point size suitable for use with `large`. This is the default value for the Dynamic Type setting. You can use this call on the default font metrics, or you can specify a text style, such as `headline`.
Apple's Developer Documentation
Applying this to our label above would look like this:
Let‘s see what this looks like for a standard UILabel
.
This is all that‘s needed to scale custom fonts with Dynamic Type. Sometimes in content-driven apps there is a need for web technologies for complex layouts / styles. Let’s see if we can support Dynamic Type and web-driven content via WKWebView
.
Dynamic Type and WKWebView
Sometimes apps need to display HTML content in a WKWebView
. What do we need to do to make sure the typography in the web content can scale with Dynamic Type? Let‘s add an HTML and CSS stylesheet snippet like this:
Unfortunately, this doesn‘t work unless you specify an Apple system font like -apple-system-body
. But we want the web view styled with the one in the HTML file. If we take what we learned above and apply that to our HTML styling it should look something like this:
Since we‘re using a web view we need to override traitCollectionDidChange(_:)
since the UIContentSizeCategory
has changed and call reloadWebView()
there.
We’ll see this example in action in the next section when we learn about environment overrides.
Environment Overrides
Luckily we can debug this without leaving the simulator. You can find that in Xcode via the Debug → View Debugging → Configure Environment Overrides. There you will see a switch to toggle text and a Dynamic Type slider to adjust the font. This is what that looks like when running the example app.
Conclusion
We observed how Dynamic Type works for native and web-based UI components. We also learned how UIFontMetrics
offloads some of the work needed to scale custom fonts. Maybe one day we‘ll see built-in custom font support for web views, but for now, UIFontMetrics
is a viable solution for font scaling.