Adaptive Design in iOS

Adaptive design exists to create UI which can adapt itself to the needs of user, device , content or context. Devices running your app can have different screen sizes and ratios, user may need larger fonts, length of string can vary for different languages. iOS have a number of techniques to develop adaptive design easily.

AutoLayout

Auto Layout is a way that lets developers create user interface by defining relationships between elements. It provides a flexible and powerful system that describes how views and the UI controls relate to each other. AutoLayout allows to develop UI totally agnostic of what context it is placed into: tiny iPhone4S screen, long iPhone 5S, huge iPhone 7+, future wide screen of iPhone 8 or squeezed manually iPad window in multitasking mode.

Without using AutoLayout, the UI controls have fixed position: the “Press Me” button’s frame origin is set to (104, 255).

With AutoLayout we define relationships between elements: the “Press Me” button is centered horizontally in it’s container view and is placed 8pt lower than "Title" label.

AutoLayout constraints can be set in InterfaceBuilder (in storyboards and nibs/xibs) or directly in code (directly creating NSLayoutConstraint, using NSLayoutAnchors or via Visual Format Language)

Precise position of UI elements is defined according to constraints in runtime by autolayout engine. Debugging can be a difficult task, but thanks to Objective-C origins of UIKit we can add breakpoints to calls of autolayout engine’s methods. You can also audit constraints in Xcode’s Debug View Hierarchy tool.

Size Classes

Sometimes you want UI elements to be laid out differently for different screen sizes or different screen orientations. For example, Safari lays out currently opened webpages vertically for portrait orientation:

iOS11, Safari, portrait mode

but for landscape orientation they are laid out in a 3-in-a-row grid: this allows to make more content visible and is more comfortable to use with two hands

iOS11, Safari, landscape mode

SizeClasses are technique to make such setup easier. There are two size classes (height and width) with three values (compact, regular, any) describing approximate size of interface. Here is short chart of size classes:

Most commonly used Size Classes

InterfaceBuilder allows to customize constraints, colors, font sizes, images and many other things according to size classes. In code that can be done with UITraitCollections and UITraitEnvironment protocol.

UIStackView

UIStackView is a container view that internally handles layout of its subviews. The layout can be configured by changing its properties axis (vertical or horizontal), distribution, alignment, and spacing. In runtime it is resolved with a number of ordinary constraints, and saves hours of developer's work by reducing the number of constraints you needed to create yourself for many common layouts.

UIStackView can dynamically add and hide it's subviews, animated.

In iOS 11 you can create stack views with custom spacing between views.

UILayoutGuides

UILayoutGuides can be included into layout instead of invisible UIViews that you can want to use in such situation:

Two same sized buttons, centered in their superview

They can be added in code only, but do not participate in view hierarchy and rendering of interface will be way faster.

Before iOS9 you would use invisible UIViews as a placeholders between buttons. Now you can replace this overhead with lightweight Layout Guides, but unfortunately not in Interface Builder

DynamicType

DynamicType is part of iOS Accessibility toolkit: user can choose preferable level of font size (make it huge, for example, if she suffers from low vision) in iOS Settings, and all apps supporting DynamicType will automatically increase fonts size. Altho not so famous among developers, this feature is significant for great amount of users.

Some text styles for different Accessibility settings

In order to support DynamicType, you have to choose one of system-provided text styles (headline, subhead, body, footnote, etc) instead of hardcoding font with predefined size. No other work is needed, but while designing UI pay attention that text can need more or less space (which is also important for multi language localization).

In iOS11 DynamicType received big update: supports custom fonts, automatically increase size of icons and images (thanks to support of vector assets), UITabBar items can show their titles in over-the-interface-toast, etc.

Best Practices — for Product Managers and Designers

  1. Make your design independent of screen size; customize it depending on size class and screen orientation instead. Rearrange elements for tiny screens as well as for huge ones. Don’t think in terms of elements’ precise locations — think in terms of relations between them.
  2. Working on UI, pay attention that text can need more or less space — either because of localization, sudden changes or DynamicType. According to IBM research, average additional space needed for comfortable localization to most European languages is:
Short texts are more likely to expand in other european languages compared to English
  1. Incorporate DynamicType! It is significant for users with low vision problem, of any age and any social group. It is cheap to implement and is almost completely automatic.
DynamicType support is crucial for users with low vision

Best Practices — for Developers

  1. Use AutoLayout in InterfaceBuilder or in code, don’t hardcode frames of elements.
  2. Default to UIStackView, it will dramatically decrease number of constraints you have to set up and maintain manually
  3. Be careful with constant distances. Don’t set hard height and width for elements with text — it will cause problems if text size will increase.
  4. If UILabel can have multiline text, set numberOfLines = 0 — that will allow label to grow as long as it's text needs. If it is unacceptable, set lineBreakMode = .byWordWrapping— that will continue decreasing font size until text length will suit label's frame or reach minimal value. By default, label have 1 line and text is truncated.

Algorithm of setting up size-class-dependent constraints in Interface Builder

  1. Build Base Layout. This is the “let’s get everything that we want on the screen” step, or the layout that we want to happen most of the time.
  2. Choose Size Class Override
  3. Uninstall Irrelevant Constraints. We’re talking Auto Layout here. You’ve got these constraints which determine the size and position of different views, and you can do this thing called uninstalling them. We’ve chosen a particular size class, and I want to take these constraints and throw them away.
  4. Add New Constraints Specific to Size Class. This is to make sure that we get the layout we actually want in this new size class.
  5. Rinse and Repeat. The important thing is to not go into your storyboard and build an iPhone portrait layout, then an iPhone landscape layout, then throw everything away before you move to iPad, etc. That doesn’t really help you at all. The approach is to start with a base layout, then work on top of it.

Written in July 2017, with respect to iOS10, some iOS11 and Xcode8.