Avatar

On a way to great accessibility with Dynamic Type

Sir, put off your monocle, today we will talk about Dynamic Type!

Accessibility plays a crucial role in all apps by far, so when you design an app make sure to keep text sizes and layout in mind for clarity and readability.

In a nutshell, Dynamic Type lets you define a desired text size in iOS user settings and use it across apps that support it. Supporting this feature not only improves the user’s experience but also trains developers to write code for more dynamic interfaces - for instance, plenty of localization options.

Today I will try to cover the following topics:

Without further ado, let’s dive into the article!


Developers write apps for users obviously and about 25 percent of them use non-default text sizes. I was really surprised seeing these statistics reading the PSPDFKit blog. And consider the fact Apple has moved the “Accessibility” settings one level up from the “General” tab in iOS 14, I decided to add metrics in my current app and got the similiar results:

Dynamic Type statistics (chart)

Just for clarification, the Accessibility fonts is an additional group with even larger text sizes for users with accessibility needs. Apple’s apps is a great example of adapting these types of fonts. They do not only change text size, but apply some layout changes - you will see it later.


Best practices

Now knowing why it’s important to pay attention adapting Dynamic Type, let’s summarize the most important practices in my opinion:

More is available on uxdesign and HIG.


Example of Accessible UI

At first, it seems effortless adapting Dynamic Type. If you follow the auto-layout guidelines properly, avoid using constant heights for UI elements, it should work as expected without any additional coding. But there’s a little number of users with accessibility fonts and you have to bear in mind them.

Important to note that at the time of writing this article iOS 14.3 is the most recent one and all further examples will be related to this version.

So let’s discover how to adapt the Accessibility UI in the right way!

1. Phone app - Favorites tab

Dynamic Type UI example

You can observe the following changes:

It is worth mentioning, you should not get rid of images all-time in similar UI. In some cases, a user does pay more attention to an image for finding needed info rather than reading text. As I said, it’s not as easy as it might seem :)

Points with the accessibility label related only to extra large fonts.

2. Phone app - Recents tab

Dynamic Type UI example

For simplicity, I will omit the general UI changes and describe the new ones:

Further example with the Mail app will demonstrate not only adjusting the position of a label but changing a color too.

3. Mail app

Dynamic Type UI example

With the above in place, this time label (“Yesterday”) has an adjusted color. It looks a lot better even with such small changes.

Keep in mind the contrast of elements!

4. Phone app - Contact tab

Dynamic Type UI example

One more example of adapting to the vertical-style UI.

Generally, I really appreciate that Apple adapts all their apps to Dynamic Type carefully. It’s a great place to be inspired and learn ways of adaptive layout.

5. Placeholdes and contextual menus

Dynamic Type UI example

You may have noticed some non-changing elements, namely the tab bar, and the navigation bar. Since generally, they are static-text elements and do not represent the main content, they stay with the same size or just with minor changes. For native elements, Apple prepared placeholders. When a user holds a finger over an element, this placeholder appears. It’s a win-win for developers and users, so keep in mind when planning to implement a custom tab bar or navigation controllers.

Dynamic Type UI example

Actually, it’s ok to apply contextual menus for custom elements as well, so the photos app does.

p.s. it looks buggy, but my point is clear :)

6. Magic switcher

Dynamic Type UI example

The last one - the most confusing. Recently I discovered the differences in iOS versions and no ideas either it’s a bug in iOS 12 or just a regression in iOS 13. Or maybe just the awareness of better readability, who knows. I’ve tried to find an explanation for this behavior with no luck. So if you have information about this - please, tell me.


It’s time to dive into practice!

I’m not going to show a step by step tutorial of how to implement Dynamic Type in your app. But I want to cover some hacks and tips for simplifying this process. If it’s really your first meeting with this topic I highly recommend to get familiar with this small article at first.

UIApplication.shared.preferredContentSizeCategory

With this info it’s enough easy to “sell” it to your managers and start adopting it.

As you can see the image has the same style using a symbol configuration. So the image is growing alongside the label automatically.

let configuration = UIImage.SymbolConfiguration(textStyle: .body)
let image = UIImage(systemName: "bookmark", withConfiguration: configuration)
imageView.image = image

label.font = UIFont.preferredFont(forTextStyle: .body)
label.adjustsFontForContentSizeCategory = true
label.numberOfLines = 0

SwiftUI is more friendly - provides Dynamic Type out of the box:

// .body style - the default
Image(systemName: "bookmark")
Text("Interesting article")

Image(systemName: "bookmark").font(.largeTitle)
Text("Interesting article").font(.largeTitle)

Readable Content Guide - our next fellow, a layout guide defines an area that can easily be read without forcing users to move their head to track the lines.

Dynamic Type UI example

The width of readable area changes according to user’s preferred content size.

let guide = view.readableContentGuide
label.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
label.leftAnchor.constraint(equalTo: guide.leftAnchor).isActive = true
label.rightAnchor.constraint(equalTo: guide.rightAnchor).isActive = true

p.s. I haven’t found a similar API in SwiftUI, except padding with different values. So would appreciate if you tell me how to configure it right.

let vew = UIView()
print(customVew.traitCollection.preferredContentSizeCategory) // iOS 12 - Unspecified
print(customVew.traitCollection.preferredContentSizeCategory) // iOS 13+ - AccessibilityXXL

My point is to have a properly configured view in both places. Depends on iOS version it’ll work different. So be aware of it!

tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = UIFontMetrics.default.scaledValue(for: 50)

Dynamic Type UI example

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)

    if traitCollection.preferredContentSizeCategory.isAccessibilityCategory {
        stackView.axis = .vertical
    } else {
        stackView.axis = .horizontal
    }
}
struct ContentView: View {
    var body: some View {
        Group {
            if sizeCategory.isAccessibilityCategory {
                VStack { ... }
            } else {
                HStack { ... }
            }
        }
    }
}
struct ContentView: View {
    @ScaledMetric(relativeTo: .body) var spacing: CGFloat = 10
    var body: some View {
        VStack(spacing: spacing) {
            (elements)
        }
    }
}

Debugging or how to make your life easier

Testing Dynamic Type can be a time-consuming process for sure - switching between Settings app for changing the font size and your app… As a result spending a lot of time to this is not what you’re planning to achieve. So let’s cover a few much simple ways of changing the font size on the fly.

Control Centre

This is a way you need when developing on a real device. Just add “Text Size” option in Control Centre. Make sure the Larger Accessibility Sizes switcher is enabled: Setting > Accessibility > Display & Text Size > Larger Text.

Dynamic Type UI example

For super-duper optimization fellows, try creating a shortcut for setting a needed text size and invoke it by triple or double tap action on a back of your phone.

  1. Create shortcut in Shortcut app
  2. Use it in accessibility menu of Back Tap: Settings > Accessibility > Touch > Back Tap (at the bottom) > Double / Triple Tap > (Created Shortut)

Adjusting accessibility via Control Centre

Accessibility Inspector

The old best friend located at Xcode > Open Developer Tool. Great to know you not only can change this settings for the Simulator, but also for any connected iOS device

Screenshot of Xcode Accessibility Inspector

Xcode

Xcode makes it easier overriding these settings directly from debugger and I hope everyone has already had time to get to know this.

Xcode Dynamic Type Setting

SwiftUI

At the end it’s quite simple to set environment for different previews and see multiple results in a row. But this approach will suit for real lucky mans, who already use SwiftUI in production. 😉

struct ContentView_Previews: PreviewProvider {
   static var previews: some View {
      Group {          
         ContentView().environment(\.sizeCategory, .extraSmall)
         ContentView().environment(\.sizeCategory, .accessibilityLarge)
      }
   }
}

I hope this was worth reading article for you! Dynamic Type is our good friend and we shouldn’t forget it.

@readaggregator