Handling Dynamic Type Changes in a Static UITableView

As I age, my ability to see small text on my iPhone gets worse. The Dynamic Type feature allows iPhone users to make the text larger on their iPhones. In this blog post, I will show you how to get Dynamic Type to work with a static UITableView.

example of dynamic type

The Problem

I discovered an issue with Dynamic Type the other day. Any screens in my app that used a static UITableView were not resizing to account for the larger label size inside the cells. Instead, the cells would be blank. The problem occurred when the user changed the Dynamic Type setting while my app was still running. (If the app was not running at the time, it would work properly.)

Even though the cell height was set to be automatic on the table view, I was still seeing a problem–clearly a bug with Apple’s UITableView.

However, the screens that used a dynamic UITableView had no issue. The cells would resize properly to accommodate the larger text.

uitableview with blank cells

How to Enable Dynamic Type

To change the Dynamic Type setting on your phone or simulator, go to Settings > General > Accessibility > Larger Text. There, you will see a switch to enable larger accessibility sizes. You do not need to turn this on, but if you do enable it, even larger text sizes will be available to you. Where you want to make the change is at the bottom of the page, where there’s a slider to set the size of the text.

accessibility setting for dynamic type

It is easy to support this setting in your app. Wherever you have a label in your app, make sure you change these two settings. First, set the text style to one of the preferred text styles provided by Apple.

Setting body text style on label

Next, set your UILabel to automatically adjust the font size to type changes.

If your label is in a UITableViewCell, make sure you have the height of all cells set to automatic on the UITableView.

The Fix for Static UITableViews

When a user changes the Dynamic Type, the system sends a UIContentSizeCategoryDidChange event that your app can listen to respond to the change. If you have the Dynamic Type setting turned on in your label, the UITableView internally adds an observer for this event and adjusts the cell heights accordingly.

This works for dynamic table views, but something is broken for static table views. The first step in fixing the problem is to remove the observer from the UITableView, so it doesn’t do whatever it is doing to make the text in the cells blank. The second step is to add our own observer to the event, so we can fix the problem ourselves.


func fixDynamicTypeForStaticTableViews() {
    // Remove the observer from the table view to prevent it from blanking out the cells
    NotificationCenter.default.removeObserver(tableView, name: .UIContentSizeCategoryDidChange, object: nil)
    // Add our own observer and handle it ourselves
    NotificationCenter.default.addObserver(self, selector: #selector(contentSizeChanged), name: .UIContentSizeCategoryDidChange, object: nil)
}

Call the fixDynamicTypeForStaticTableViews in your viewDidLoad function. Now that we have removed the observer from the table view and added our own, we need to implement the contentSizeChanged function that will get called when the event is fired.

All we need to do in this function is tell the table view to reload itself. The cells will get resized appropriately, and the text inside the cells will respond to the Dynamic Type setting. Notice that I have the @objc attribute on the function that indicates this code will be called from Objective-C.


@objc func contentSizeChanged() {
    tableView.reloadData()
}

That is all that we need to do to fix static UITableViews. Let me know if this fix helped you out.