Peter Boni

Trying to not take myself too seriously.

iOS 7 Dynamic Type

iOS 7 introduces Dynamic Type, which if apps choose to support it, gives the user control over text size in an app. Web pages have had this feature for free since the early days of the web, but web browsers moved away from dynamic text sizing. The Two Types of Browser Zoom.

iOS7 Dynamic Type automatically adjusts weight, letter spacing, and line height for every font size. It looks stunning, but there’s work to make your app support it.

Default Size

Here’s what UserXListView looks like with the Text Size (Settings – General – Text Size) set to the default size in the iPhone Retina (3.5-inch) iOS Simulator.

We get 3 & ‘a bit’ cells in the UITableView.

Smallest Size

Changing the Text Size to the smallest size means we can fit slightly more.

Nearly 4 cells in the UITableView.

Largest Size

Changing the Text Size to the largest size means we can fit slightly less, but get big beautiful type.

3 cells in the UITableView.

Implementation, The Easy Part

That’s how it looks, but how hard is it to make your app support it. Let’s look at UserXListView. The cell layout makes use of Auto Layout Constraints, which are very powerful.

iOS 7 has defined Text Styles – Body, Caption 1, Caption 2, Footnote, Headline 1, Headline 2, Subhead 1, and Subhead 2. We assign a Text Style to each label in the cell layout via the attribute inspector as shown below.

Once the label Text Styles & Auto Layout Constraints are set, we’re ready for the hard part.

Implementation, The Hard Part

Unlike Auto Layout in this example, there’s more work to do to support Dynamic Type.

We need to make the Controller aware of a Text Size change by hooking into the UIContentSizeCategoryDidChangeNotification.

1
2
3
4
5
6
7
8
9
10
11
- (void)viewDidLoad {
    [super viewDidLoad];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(preferredContentSizeChanged:) name:UIContentSizeCategoryDidChangeNotification object:nil];
}

- (void)preferredContentSizeChanged:(NSNotification *)aNotification {
    // adjust the layout of the cells
    [self.view setNeedsLayout];

    // refresh view...
}

We need to calculate the height of the row/cell based on it’s content, and as each label in the cell can change height we need to calculate the total height of the cell required.

1
2
3
4
5
6
7
8
9
10
11
12
13
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    Account *account = [self.fetchedResultsController objectAtIndexPath:indexPath];

    UIFont *nameLabelFont = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline1];
    CGSize nameLabelFontSize = [[account name] sizeWithAttributes:[NSDictionary dictionaryWithObject:nameLabelFont forKey:NSFontAttributeName]];

    // ...

    CGFloat PADDING_OUTER = 10;
    CGFloat totalHeight = PADDING_OUTER + nameLabelFontSize.height + ... + PADDING_OUTER;

    return totalHeight;
}

We also need to programmatically assign (or re-assign because we already did it in the Storyboard) the cell label Text Style values.

1
2
3
4
5
6
7
8
9
10
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    AccountCell *cell = (AccountCell *)[tableView dequeueReusableCellWithIdentifier:@"AccountCell"];

    Account *account = [self.fetchedResultsController objectAtIndexPath:indexPath];

    cell.nameLabel.text = [account name];
    cell.nameLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleHeadline1];

    // ...
}

Final Thoughts

That’s it. iOS 7 determines & applies the font attributes for each label based on the Text Style for each label, and the iOS 7 Text Size system setting selected by the user, and it’s left to us (the developer) to cater for the changes in the layout to the view.

As you can see there’s a bit of extra work to support Dynamic Type. It would be nice if it was a bit smarter, a bit more automatic, but with great power comes great responsibility.