SwiftUI Accessibility: Dynamic Type

Rob Whitaker
4 min readSep 18, 2019

--

Like all accessibility features, Dynamic Type is about customisability. Many of your customers, and maybe even you, are using Dynamic Type without even considering it an accessibility feature. Dynamic type allows iOS users to set the text to a size that they find comfortable to read. This may mean making it a little larger so it’s easier to read for those of us who haven’t yet accepted we might need glasses. Or it could mean ramping it right up for people with low vision. Or taking the text size down for extra content and privacy.

Like many accessibility features on iOS, Dynamic Type support has been greatly improved in SwiftUI. There are a few things you should do (and not do) to make the most of it.

Do

Nothing

SwiftUI supports dynamic type by default and is multi-line by default. So if you add Text("Some text") to your view, you’re done.

Text Styles

Text is Body style by default, which makes it great for the majority of uses. But any app with a single text style is going to look pretty boring. Fortunately, Apple provides a selection of 11 type styles for you to use. Each of these styles supports dynamic type, and adjusts the size, leading and trailing for you as needed. Using too many type styles can lead to your app looking messy and inconsistent. If you find these 11 aren’t enough, it might be worth taking another look at your designs.

Dynamic Text sizes

You can choose your required text style using the modifier .font(.headline) to set your text to the headline style. The full list of type styles can be found in Apple’s developer documentation.

Custom Fonts

Apple’s built-in Dynamic Type text styles all use the default San Francisco font. SF is a great font for iOS, but to make your app stand out you need a custom font.

Some devs have told me they don’t support Dynamic Type in their app because it doesn’t support custom fonts. This isn’t the case, Keith Harrison over at Use Your Loaf has a great post on using custom fonts with Dynamic Type in UIKit that I highly recommend.

Unfortunately, these methods won’t work for us in SwiftUI. Custom font support for dynamic type in SwiftUI needs some improvement from the current version. There is, however, a simple way we can leverage some of the built-in text style’s dynamic type support. By adding a helper method, we can get the current point size for our desired text style.

func textSize(textStyle: UIFont.TextStyle) -> CGFloat {
return UIFont.preferredFont(forTextStyle: textStyle).pointSize
}

Then we can use the custom font modifier to apply this to our text.

.font(.custom("MyCustomFont", size: textSize(textStyle: .headline)))
Custom font

The downside of this approach is this fixes the text size until the screen is redrawn. So if your customer changes their dynamic type size your text won’t change until this screen is recreated.

Don’t

Line Limit

It’s possible to limit the number of lines your text wraps to using the modifier .lineLimit(1). Meaning if your text requires more lines it will end in an ellipsis. This is a poor choice as your users with the largest text sizes are likely to lose the full meaning. As a rule, your UI should be able to accommodate whatever text content at whatever size its provided with. If you find your screen or control can’t handle this, it’s worth taking another look at your design, or how you’ve built it.

Truncated text

Fixed sizes

There are two ways of fixing a font size. .font(.system(size: 17)) or .font(.custom("MyCustomFont", size: 17)). Please don’t be tempted to use these. If your customer has chosen to set their preferred text size, either for accessibility reasons, or because they like it that way, it’s pretty arrogant as an app developer to ignore this — at least, that’s how your customer will see it.

Fixed text size

As with the line limit, if you find your screen or control doesn’t work with larger fonts re-visit the design or how you’ve built it. Sometimes it’s not possible to support larger text sizes for certain controls. See iOS standard tab bar for example. In these situations, we can add a function that calls our dynamic size function above, and provides maximum value. This allows your text to scale down but limits how large it can become.

func textSizeForThisOneSpecificUse() -> CGFloat {
return fmin(textSize(textStyle: .body), 28.0)
}

--

--

Rob Whitaker

iOS Engineer at Capital One. Author, Developing Inclusive Mobile Apps, Apress. https://amzn.to/3aNRQ6d