Supporting Dynamic Type

Dynamic type is an essential iOS feature that allows the user to choose their preferred text size. Find out what it means and how you can support it in your App.

Last updated: May 19, 2021

Dynamic Type

Apple first introduced dynamic type way back in iOS 7. It provides the user with the ability to specify their preferred text size in the Display & Brightness settings of the device. Five extra large text sizes are also available from the Accessibility settings.

Text Size Settings

When you add support for dynamic type to your applications the user expects you to adjust your text for their preferred reading size:

Text Styles

Apple introduced the UIFontDescriptor class with iOS 7 to provide a way to describe the attributes of a font such as the font family, size and whole set of other attributes. This makes it easier to create new UIFont objects based on a common set of attributes. One of the attributes introduced with iOS 7 was the text style.

A text style is a pre-defined constant that provides a short hand way of creating a UIFontDescriptor or by extension a UIFont with a predetermined set of attributes. There were six text styles introduced with iOS 7:

.headline  .subheadline  .body
.footnote  .caption1     .caption2

In iOS 9, Apple added four more styles:

.title1    .title2       .title3
.callout

In iOS 11, Apple added the large title style:

.largeTitle

The UIFont class includes a class method that returns an instance of the font with the given text style. For example, to create a font suitable for use as a headline:

// Swift
let myFont = UIFont.preferredFont(forTextStyle: .headline)

If you’re using Objective-C the syntax is a little more verbose:

// Objective-C
UIFont *myFont = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline];

The important point to understand with these preferred fonts is that Apple decides the details for the font including the user’s preferred text size. The screenshots below show how the font for each text style changes for the extra-small, default extra-large and the extra-extra-extra-large setting of the text size:

Dynamic type text styles at three different sizes

In iOS 7 Apple used the Helvetica Neue font family for the preferred font. That changed to Apple’s own San Francisco font in iOS 9. In both cases, Apple takes care of sizing, spacing and other attributes to make the text legible for all text styles and sizes.

Text Styles In Interface Builder

There is full support for text styles in Interface Builder when creating UIKit controls. For example, for a UILabel object the font setting in the attributes inspector allows you to directly set the text style used for the label:

Selecting the Title 1 text style in Interface Builder

Implementing Support For Dynamic Type

To show how to support Dynamic Type I have created an example app that has a stack view containing a UILabel for each of the possible text styles (excluding the large title which needs at least iOS 11). The view controller as it appears in Interface Builder is shown below.

A stack view in Interface Builder with ten labels, one for each text style

Each text label has the corresponding text style directly set in Interface Builder:

Title 1 Label with font set to Title 1 style

If you’re minimum deployment target is at least iOS 10, make sure to set the “Automatically Adjusts Font” checkbox. The system will then automatically update the font size anytime the user changes their preferred text size.

Since the size of each of the text labels can vary at runtime we need an adaptive layout. In this case I’ve embedded my stack view in a scroll view to allow the stack view to extend beyond the height of the device. Take a look at Scroll View Layouts With Interface Builder for a detailed walk through of this type of layout.

Note: It is not mandatory to use Auto Layout with preferred fonts but it does make life a whole lot easier. With dynamic type and preferred fonts the size of a text field can change anytime the user changes the text size setting. Managing this yourself is likely to be a frustrating experience.

If You Need To Support iOS 9

To support dynamic type with iOS 9, or earlier, you need to update the preferred font of your text any time the user changes the size. You can do that by listening for the content size category did change notification and manually updating the font of any labels, text fields and text views.

To avoid build warnings you’ll need to uncheck the “Automatically Adjusts Font” setting in Interface Builder:

Interface Builder Inspector with Automatically Adjusts Font unchecked

We can check the availability of the setting in the viewDidLoad method of the view controller. If we are running iOS 10 or later we can set the property in code for each of the labels. Otherwise we create an observer for the notification:

// Swift
override func viewDidLoad() {
  super.viewDidLoad()
  
  if #available(iOS 10, *) {
    allLabels.forEach {
      $0.adjustsFontForContentSizeCategory = true
    }
  } else {
    NotificationCenter.default.addObserver(self,
      selector: #selector(updateTextStyles(_:)),
      name: UIContentSizeCategory.didChangeNotification, 
      object: nil)
    }
}
// Objective-C
- (void)viewDidLoad {
  [super viewDidLoad];
  if (@available(iOS 10, *)) {
    for (UILabel *label in self.allLabels) {
      label.adjustsFontForContentSizeCategory = YES;
    }
  } else {
    [[NSNotificationCenter defaultCenter] addObserver:self
      selector:@selector(updateTextStyles:)
      name:UIContentSizeCategoryDidChangeNotification
      object:nil];
  }
}

Then in the method that handles the notification we update the font of each of the labels:

// Swift
@objc private func updateTextStyles(_ notification: Notification) {
  title1Label.font = UIFont.preferredFont(forTextStyle: .title1)
  title2Label.font = UIFont.preferredFont(forTextStyle: .title2)
  title3Label.font = UIFont.preferredFont(forTextStyle: .title3)
  ...
}
// Objective-C
- (void)updateTextStyles:(NSNotification *)notification {
  self.title1Label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleTitle1];
  self.title2Label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleTitle2];
  self.title3Label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleTitle3];
  ...
}

The lack of auto-adjusting font sizes makes supporting dynamic type on iOS 9 and earlier a bit of a pain.

What About Other Fonts?

If you want to use your own custom fonts with dynamic type take a look at this post (requires at least iOS 11):

Get The Code

You can find the example Dynamic Text Xcode project in my GitHub CodeExamples repository.

Get The Book

If you want learn more about using dynamic type to build adaptive layouts take a look at my book Modern Auto Layout.