Welcome back! This is the third episode of “The Ultimate Guide to iOS AutoLayout”. AutoLayout is a set of rules that dictate how the visual elements of an iOS App display on iOS devices. These rules can be defined visually or programmatically, and the developer needs to define them for every single control or visual element included in the App. In the first episode, we started by mastering the basics and learning about how to avoid conflicts and ambiguities. For the second episode, we delved into responsive AutoLayout interfaces by switching from fixed dimensions to responsive positioning and sizing. In this episode, we are going to learn about size classes.
In 2014, Apple introduced the new iPhone 6 and iPhone 6Plus. These phones featured bigger screens than any other previous iPhone. Much bigger. In fact, the iPhone 6Plus, with its 5.5 inch display, could more aptly be called a phablet than a phone. With this array of different screen sizes and proportions (3.5, 4, 4.7, 5.5, 7.9, 9.7, and later the bigger iPad Pro 12.9), AutoLayout was no longer enough to completely define our user interfaces. Therefore, following a similar approach to Android Layouts, Apple introduced a new concept for grouping dimensions into easily manageable categories, the “Size Classes”.
Thus, Size Classes are an abstraction of how a device should be categorized depending on its screen dimensions. Apple defined two categorizations for both vertical and horizontal sizes called “Regular” and “Compact”. The former specifies a big space, while the latter specifies a smal” space. How big or small exactly? Here, “big” and “small” are not intended to be measured in inches.
Instead, think on size classes as an abstract, intuitive way of defining the proportions and dimensions of a screen or view. The figure above, from Apple, shows you the exact size classes for every device. Note how the iPhone 6(s)/7 Plus significantly differs from the rest of iPhones by having a regular width on landscape. This is the reason why you can have a sidebar icon row on the iPhone 6S/7 Plus.
When approaching the design of your App, you will start with a generic design, and put together the first user interface, like we did in the previous episodes. Then, you will adapt this interface to the different combinations of Regular/Compact device dimensions when needed. One of the best things about Size Classes is its inheritance mechanism. The inheritance means that the constraints that you define for less restrictive size classes propagate to more restrictive size classes unless overridden. Therefore, you can design for “Compact width – Any height”, and your design will propagate for both compact height and regular height.
Apple introduced the concept of Size Classes beautifully in Xcode, with a default 600×600 square canvas that is smaller than an iPad, larger than an iPhone, and different to both in proportions. I think it’s a nice move as it forces you to step back from a concrete device representation, and think in terms of a generic device.
If you have been following the previous episodes, you might wonder why use size classes at all. Our interface looked pretty well from an iPhone 4S to a big iPad Pro, right? Well, not completely. Let’s take a look at how our screen looked in several device types:
While the interface looks acceptable, you will notice that the fonts are somewhat unbalanced. They look great in the iPhone 7 screen, but they are too big in the iPhone 5. They are certainly too small too for the iPad’s screen. You could try to do some calculations and adjust the font size programmatically, and that’s what we did before Size Classes, but that’s not an optimal solution. With Size Classes, it’s easy to modify the constraints for specific sizes and clearly see the results at a glance. Depending on your background and tastes, you might notice other things too. For instance, the “Contact” button is probably ok for the phones, but too big for the iPad’s screen.
Let’s start with a simple introduction to how Size Classes works. First, you need to make sure that size classes are enabled for the Storyboard file or the Xib/Nib file that you are editing. To check this out, just go to the Storyboard/Nib/Xib file and, in the file inspector, look for “Use Trait Variations”. If it’s enabled (AutoLayout needs to be enabled as well), then you are good to go. Once they are enabled, you will see a bar at the bottom listing all different devices and possible configurations (rotations and partial screens for iPads).
Normally, all new projects that you create under Xcode 8 and up will have size classes enabled for the main and launch screen storyboards. Furthermore, all new storyboards and views that you create will have size classes enabled by default. If you disable them (by unchecking the “Use Trait Variations” checkbox), Xcode will ask you to choose the device the device size to adopt. From that moment, that IB file will only work for one device. You can always re-enable back the trait variations (a.k.a Size Classes) anytime.
Once we are sure that our IB file supports size classes, it’s time to check the properties that are subject to change for different size classes. You will easily spot them because they have a “+” sign at the left in the Attributes Inspector. Take your time to go through the different visual controls and constraints in our screen. You will notice that both have lots of properties that can be modified for Size Clases. Notice also the “+ Installed” option in all visual controls and constraints. This “installed” property specifies whether a control or constraint is “enabled” for certain size class. If not “installed”, the control will not be included in the hierarchy, or the constraint will not be applied.
We will focus first on the fonts for the labels in our screen. The video below shows you how to add a variation to distinguish between regular and compact sizes. In this case, we are going to set the “normal” font size to 34, and add a “compact” size class variation to 20.
Notice how the font switches to 20 when selecting the iPhone or the 2/3 Split View of the iPad (both having a compact width size class). Now let’s repeat the process with the email label. We’ll change the font size to 16 and add a “compact width” variation, setting the font size to 12. We’ll also repeat this process for the “Favorite…” headers, setting their font size to 24 normally, and 20 for compact width.
Of course, you can do this with lots of other properties, from constants, offsets to colors and font types.
To show how to completely modify the constraints between two size classes, let’s suppose that we want our button to be smaller for iPads. The button will take the whole width of the screen for iPhones, but it will have a fixed width of 240px for iPads. The following video shows the process for achieving this configuration.
First, we add a horizontal center constraint for out button. This will help us handle the constraints when we start “removing” them in some size classes, and does not affect our general design. Now, we need to add size variations for the leading and trailing constraints of our button. Those constraints are binding the button horizontal edges to the map above. We add size variations for “Compact width” and mark the constraints as “installed” only for the Compact width scenario.
Thus, when we get to the “Regular” size (the iPad in full screen), we see that our button did shrink to its minimum size. We then add this width constraint for the button, setting it to 240. We want this constant to be active only for the “Regular width” size class, otherwise, it will conflict with the leading/trailing edge constraints that are active for that configuration. Thus, we add a variation for “Compact width” and “uninstall” the width constraint for the “Regular width” size class.
Now, switching between Regular and Compact width size classes gives us the desired results. The button expands to both edges on small screens (Compact width size classes), but stays at a maximum size of 240 in the big ones. Congratulations! Our user interface is really looking nice now.
The main problem with Size Classes is, in my opinion, that two sizes don’t fit all. The iPhone 6/6S/7 Plus, for instance, is quite a big screen, but it’s still classified as “Compact width” on portrait, equating it with an iPhone 4S. It’s obvious that both screens are completely different in size, and the iPhone 6/6S/7 Plus is closer to an iPad. This have some annoying consequences when assigning constraints and properties for compact size classes. I usually need to adjust things for the plus. I think Size Classes would greatly benefit from the inclusion of a category in-between compact and regular. Ideally, though, there should be a way of specifying font sizes, pixels and dimensions in a non-discrete, responsive way.
This problem comes to the fore specially for font sizes in labels, buttons, textfields, etc. In my opinion, Apple should explore alternatives to discrete categories, like Android does. Android makes a clever use of “Scale-independent Pixels”, a measure that allows to specify font sizes (among other things) in a more generic way. I’m really looking forward to Apple releasing something similar.
In this episode of “The Ultimate Guide to iOS AutoLayout”, we learned about Size Classes, and how they can improve our user interfaces. By making use of Size Classes, we can really fine-tune some aspects of how our designs display on the different iOS devices. It’s worth investing some time in learning how to get the most out of them. Don’t worry if they seem a little complex at first. Just give it some time, play and experiment with your visual controls and constraints. You will eventually become used to them.
As always, you can find the full source code for the sample project in my Github Repository. In the next episode of the series, we will talk about how to add, define and modify constraints programmatically, via NSLayoutConstraint. We will learn, with examples, why sometimes you need to “get your hands dirty” and code your constraints programmatically.