Restricting Dynamic Type Sizes

In iOS 15 you can set limits on the minimum and maximum sizes of dynamic type. It works for both UIKit and SwiftUI.

// UIKit
view.minimumContentSizeCategory = .medium
view.maximumContentSizeCategory = .accessibilityExtraLarge

// SwiftUI
ContentView()
  .dynamicTypeSize(.medium ... .accessibility3)

Minimum and Maximum Content Size

In iOS 15 Apple added two properties to UIView that let you limit the minimum and maximum content size of dynamic type:

var minimumContentSizeCategory: UIContentSizeCategory?
var maximumContentSizeCategory: UIContentSizeCategory?

When you set one or both of these properties on a view it limits the dynamic type size for that view and any of its subviews. Setting the property to nil removes the limit. If you set an invalid range, so that the minimum is greater than the maximum, only the maximum value applies.

For example, to limit the dynamic type size in a view hierarchy to the range .medium to .accessibilityExtraLarge:

view.minimumContentSizeCategory = .medium
view.maximumContentSizeCategory = .accessibilityExtraLarge

Since these are properties of UIView they are also available on subclasses like UILabel and UITextView. This gives you fine grained control to limit an individual text element without affecting other text:

pubLabel.minimumContentSizeCategory = .large

Debugging Tip

For debugging purposes, there’s a property on UIView that gives you a description of the content size a view is using together with any limits applied to it or inherited from a superview:

print(pubLabel.appliedContentSizeCategoryLimitsDescription)

For example, with my preferred content size set to accessibility-medium (AX-M), and an upper limit on a label set to XXXL, the label is limited to XXXL:

 3. <UIView:0x155f175f0>: - <= (none->)AX-M <= XXXL
 2. <UIStackView:0x155f11c00>: - <= (AX-M->)AX-M <= XXXL
 1. <UILabel:0x155f112e0>: - <= (AX-M->)XXXL <= XXXL
--> XXXL

Adding an upper limit on the root view to XL results in limiting the label to XL:

 3. <UIView:0x14ad145c0>: - <= (none->)XL <= XXXL
 2. <UIStackView:0x14ad0e170>: - <= (XL->)XL <= XXXL
 1. <UILabel:0x14ad27680>: - <= (XL->)XL <= XXXL
--> XL

SwiftUI Dynamic Type Ranges

For SwiftUI, you apply a dynamic type size range as a modifier to a view. For example, to limit text to be no bigger than .accessibility1:

// Limit text to no bigger than accessibility1
Text("Hello, world!")
  .font(.title)
  .dynamicTypeSize(...DynamicTypeSize.accessibility1)

If you want to set a lower limit:

  // No smaller than .large
  .dynamicTypeSize(.large...)

Or to keep the text within a range:

  // No smaller than large, no bigger than accessibility1
  .dynamicTypeSize(.large ... .accessibility1)

If you set multiple ranges the result is their intersection. If you forget the range and set a single value the text appears at that size. Probably not what you want other than as a quick way to preview text at a certain size:

  .dynamicTypeSize(.accessibility1)

Don’t Limit Readability

My fear when I saw this feature was that it might tempt developers to use it instead of creating layouts that work at the largest accessibility text sizes. Here’s a quote from the Apple engineer in the WWDC session:

Please do not use this API to unduly limit text size. These settings serve an extremely important function, and it’s paramount that your app’s functionality is all available, and everything is legible, to people using the highest text size setting.

In other words, it might be ok to limit the size of a title or headline that is already large. Don’t use this as a way of fixing problems with your layout.

Learn More

See the following WWDC sessions:

If you want to learn more about building adaptive layouts with dynamic type you might like my book - Modern Auto Layout.