from :http://captechconsulting.com/blog/john-szumski/ios-7-tutorial-series-dynamic-type
Apple debuted many new typographic APIs with iOS 7 and has made its new text options very apparent to end users by changing the default system font. Text Kit brings many powerful features of CoreText to a higher level of abstraction and dramatically expands the approachability of the text layout features that have been available to developers for some time. Along with the new system font, Apple has also added Dynamic Type, a feature that gives users the ability to choose a base text size that is used throughout the system in both first- and third-party applications. Changing font sizes poses some new challenges for developers, however if you are already using Apple-suggested technologies like Auto Layout and localized strings your app may already have some of the necessary flexibility. The sample application included with this post demonstrates how to adapt to changes in the base text size, how to use custom fonts with Dynamic Type, and best practices to use in your application.
Using the System Content Size
The system content size is exposed to users in Settings > General > Text Size as a slider with seven different base sizes where the default size roughly matches the font sizes previously recommended for iOS 6 and below.

The NSString
value for the current setting is available to developers as the preferredContentSizeCategory
property of UIApplication
. Typically you won't need to use this value directly unless you are using a custom interface font, which is discussed in a later section of this post. Instead, you should use a new UIFont
method preferredFontForTextStyle:
where you previously would have used systemFontOfSize:
or boldSystemFontOfSize:
to create a UIFont
instance. This method takes one of six text style options that will allow Apple to choose an appropriate font, weight, and size adjusted for the user's current content size:
-
UIFontTextStyleHeadline
-
UIFontTextStyleSubheadline
-
UIFontTextStyleBody
-
UIFontTextStyleFootnote
-
UIFontTextStyleCaption1
-
UIFontTextStyleCaption2
These six style options combined with the seven content size options yield a large number of font permutations, each of which has been meticulously tweaked by Apple designers for optimal display. Because of the font size variances possible at runtime, apps must be structured to have fluid layouts and avoid hardcoded width or size values.
Responding to Content Size Changes
When an iOS application is loaded for the first time, its user interface will be drawn with the system's current content size at launch time, however developers can't rely on this value staying constant throughout the lifetime of the application. Users can use the multitasking features of iOS to switch to Settings and update the text size setting at any time. To respond to these changes at runtime, your controllers and views will need to listen for theUIContentSizeCategoryDidChangeNotification
:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(userTextSizeDidChange) name:UIContentSizeCategoryDidChangeNotification object:nil];
Your implementation of userTextSizeDidChange
will need to update fonts on all visible UI elements and recalculate a layout for the updated sizes of those elements. It's important to note that you must apply a new UIFont
instance withpreferredFontForTextStyle:
to get an updated size. Simply calling invalidateIntrinsicContentSize
orsetNeedsLayout
will not automatically apply the new content size because UIFont
instances are immutable.

For UITableViewController
subclasses, both steps can be accomplished with a call to reloadData
orreloadRowsAtIndexPaths:
for the visible cells. The table controller will recalculate row heights for the new text size andtableView:cellForRowAtIndexPath:
will apply the new fonts. Note that this approach requires fonts to be set incellForRowAtIndexPath:
and not in an init method. An alternative is to have each cell instance listen forUIContentSizeCategoryDidChangeNotification
and adjust its fonts internally, and then the controller is only required to update the layout.
For UIViewController
s, UIScrollView
s, or plain UIView
s, apply the new fonts to your interface elements. If you are using Auto Layout, adjusting the fonts will be enough to activate the layout engine and will also calculate a newcontentSize
for UIScrollView
s. For manual layouts, call setNeedsLayout
to tell iOS to update the positions of all subviews. The sample application uses Auto Layout, and I highly encourage you to adopt it if you haven't already.
It is also important to note that these adaptive fonts can give non-optimal layout values if used directly. For example, the sample application's table view implements boundingRectWithSize:options:attributes:context:
to determine the appropriate height for the row:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { NSString *codename = self.codenames[indexPath.row]; CGRect codenameRect = [codename boundingRectWithSize:CGSizeMake( CGRectGetWidth(CGRectIntegral(tableView.bounds)) - 40, MAXFLOAT) // 40 = 20pt horizontal padding on each side options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName: [self fontForBodyTextStyle]} context:nil]; return MAX(44.0f, CGRectGetHeight(CGRectIntegral(codenameRect)) + 20); // 20 = 10pt vertical padding on each end }
If you put a breakpoint in this method and examine codenameRect
, you'll notice that it has a size that includes fractional points:
(lldb) p codenameRect (CGRect) $1 = origin=(x=0, y=0) size=(width=101.43001, height=16.702)
These values can be used for layout, however using fractional point values forces the device to interpolate and blend pixels when drawing to the screen. This extra processing can have noticeable effects if done across many subviews, especially while scrolling. The fix is easy: simply call CGRectIntegral()
on any CGRect
before you use it for layout calculations. If you are using Auto Layout, the fix is even easier; the layout system takes care of fractional points automatically.
Using Custom Fonts with Dynamic Type
Custom fonts are a great way to have your app stand out from the crowd, and fortunately it's very easy to support Dynamic Type with a simple category on UIFont
. If you need a refresher on using custom fonts on iOS, this Stack Overflow post covers the basics and iOS Fonts lists all the available fonts included with the system. When implementing Dynamic Type with a custom font, the basic idea is to have a mapping between the UIContentSizeCategory
string constants and a font size. In the sample project I created a category called AvenirContentSize
that addspreferredAvenirFontForTextStyle:
to UIFont
:
+ (UIFont *)preferredAvenirFontForTextStyle:(NSString *)textStyle { // choose the font size CGFloat fontSize = 16.0; NSString *contentSize = [UIApplication sharedApplication].preferredContentSizeCategory; if ([contentSize isEqualToString:UIContentSizeCategoryExtraSmall]) { fontSize = 12.0; } else if ([contentSize isEqualToString:UIContentSizeCategorySmall]) { fontSize = 14.0; } else if ([contentSize isEqualToString:UIContentSizeCategoryMedium]) { fontSize = 16.0; } else if ([contentSize isEqualToString:UIContentSizeCategoryLarge]) { fontSize = 18.0; } else if ([contentSize isEqualToString:UIContentSizeCategoryExtraLarge]) { fontSize = 20.0; } else if ([contentSize isEqualToString:UIContentSizeCategoryExtraExtraLarge]) { fontSize = 22.0; } else if ([contentSize isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge]) { fontSize = 24.0; } // choose the font weight if ([textStyle isEqualToString:UIFontTextStyleHeadline] || [textStyle isEqualToString:UIFontTextStyleSubheadline]) { return [UIFont fontWithName:@"Avenir-Black" size:fontSize]; } else { return [UIFont fontWithName:@"Avenir-Medium" size:fontSize]; } }
Now wherever I want to use Avenir in my application I can use that method just as I wouldpreferredFontForTextStyle:
to use the system font. You'll notice that I'm scaling the fontSize
linearly, however you can certainly get fancy by adjusting along a curve or interchanging different weights within the same font family. Similar adjustments can be made for the textStyle
argument to make headlines bold or use a different font variant.
Notable Limitations
I've come across two limitations of the Dynamic Type system:
-
UITextField
: The placeholder font is not exposed in the public API and doesn't use the value of thefont
property, therefore you can't adjust its size as the content size changes. This leads to a weird effect where the text adjusts normally but if the field is cleared it jumps back to the original font size when displaying the placeholder. You can most likely fix this by subclassingUITextField
and implementingplaceholderRectForBounds:
anddrawPlaceholderInRect:
with adaptive fonts yourself, however this is left as an exercise for the reader.
-
MKAnnotationView
: The text in the system-provided callout does not adjust as the content size changes. Unfortunately, fixing this requires you to implement the entire callout yourself.
Even with these relatively minor limitations, the Dynamic Type system is great for improving the accessibility of your application to users with vision impairments. Implementation only requires an easy UIFont
category and oneNSNotification
, so developers have little reason to skip Dynamic Type support.
The sample application is available on GitHub.