Lesson 3 - Swift UI for different screen sizes and Autolayout
In the previous lesson, Building the UI and introduction to basic components, we created our first Swift app in Xcode. However, it could only display a text label or some other Apple control which we described last time.
In today's tutorial, we'll look at very important part of building the UI, which is making the components on the screen aligned where we want them to be.
You probably know that modern websites nowadays can handle different screen sizes (desktop, tablet, cell phone..) and adapt to them. The situation is not that extreme for iOS, but we still have two iPhone sizes (+ the new X model), several iPads and iPhone SE is also still relevant (has the same size as the 5 and 5s models). There are already many display sizes and we should take them into account.
We can make our work easier making the app available for iPhones only. After all, there are less iPads there, but we still have to have different screen sizes in mind. So what should we do? The answer is Autolayout which is the way of defining a responsive UI, adapting to the display size.
Layouts
If all devices had the same screen size, we could simply put the components at fixed coordinates (e.g. put a label to 20px on the X axis and 40px on the Y axis). As soon as the screen size differs, we have to reposition the controls in the controller to make them fit or to avoid them using only a small portion of the screen. Layouts provide us with this mechanism.
Autolayout
The first layout we'll introduce is Autolayout
. The position of
the controls is defined by constraints. In some programming
languages, these are called anchors, because constraints are
often used to attach a side of the component to the outer container side. We'll
stick with the "constraint" term here.
To make the Autolayout
work correctly, every component must
define how it should be positioned in consideration to other components or to
the edges of the particular controller's UI. There are several ways to achieve
this. We can align a component to both axes, define its distance from other UI
controls and so on. We'll show everything step by step. In different situations,
different approaches are needed.
Autolayout
can be complex for beginners, but it's
still the only reasonable way to design the UI so don't avoid it and don't give
up on Autolayout
after several unsuccessful attempts.
Practical example
Let's open Main.storyboard
, we can use the project with the
Label
from the last lesson. Or you can create a new project and add
some UI components to it. It doesn't matter since we're going to work within
just this single file for now. Let's select Label
, for example, and
align it to the center using constraints. We can do so using the icons in the
bottom right corner of the editor.
Component alignment
We use the Align constraints icon with two rectangles in the center for alignment.
We'll check Vertically in Container and Horizontally in
Container which makes the Label
centered in both directions.
Then we'll just click the Add 2 Constraints assigning the appropriate
alignment constraints to it. The term container refers to the component in which
the control is nested. In this case, this refers to the controller itself. If
the Label
was e.g. inside of the View
component, which
is by the way often used as the container for other controls or e.g. for
detecting touch gestures, the View
would be the container. This
way, the Label
is aligned and will be displayed in the center on
all screen sizes. It's real position (X and Y) will be changed depending on the
screen size of the given device.
The result:
Have you been wondering why are we allowed to enter a particular number,
which was 0
by default? In case we wanted to move the
Label
to one side, we'd set such a value. E.g. move it a few pixels
vertically above the center. You can try editing the already assigned
constraints in the Size inspector. Since the coordinates start from the top left
corner, we would move the Label
upwards by setting the Y axis
constraint to e.g. -30
.
Attaching to sides and limiting the size
Align constraints are especially useful for centering. Now, let's have a look
at position and size constraints. Here, constraints for all four sides have to
be set. Or you can set only two side constraints and define the width and height
of the component. (We can avoid specifying the height using Aspect
ratio.) Only like this the Autolayout
will know where to put
our component at and what size set it to. Of course, you can't set, for example,
constraints for left and right sides and set a fixed width at the same time.
These constraints would be in conflict with each other because attaching the
component to the container on both left and right would make the component
stretch.
Let's try to position our Label
to the top right corner. Select
it and then click the icon next to the alignment one, which kinda looks like a
Tie Fighter.
This dialog box looks more complicated, but there's nothing to be afraid of. We're interested in the top right corner, so we're going to align from the top and right side. I recommend unchecking Constrain to margins which counts with some inner edges and sometimes doesn't work correctly. I personally encountered the margins not being registered. Also, if you don't use them, you won't make a mistake in sizes etc.
Let's position the Label
e.g. 40
pixels from the
top edge and 40
pixels from the right one. You can recognize active
constraint by the solid red line. Clicking in toggles between active/inactive
and also activates entering a new value. Don't forget to set the height and
weight, since we've attached the Label
only to the top and bottom
edges. Otherwise, its size wouldn't be defined. Then just click Add 4
Constraints to anchor the Label
. Now it stays in the top right
corner. You can try it out by switching between different screen sizes - you can
set this in the bottom part of the editor.
Now we introduced the two basic approaches, but that's not all
Autolayout
can do.
More components
If you have multiple components in the container, Autolayout
takes them into account when creating constraints. Now, if we add another
component, e.g. a Button
, the distance constraint for the right
edge of the Button will be calculated from the Label
, which makes
sense. However, you can choose to make it calculate from the controller's edge
again (the down arrow in the value field). I don't recommend it
though.
Before you start setting constraints to other elements like this, it's worth
thinking about the overall design and if you will add more components soon or
change their position. Don't be afraid of using View
as the
container for components that relate to each other. You'll then set the
constraints from the edges of this View
and you can move the view
itself easily. You can also temporarily set distinct background colors to
different View
s to see how they are positioned.
Conflicts between constraints
Sometimes it may happen that Autolayout
will get "mad at
us" for bad constraints. In that case, you may have forgotten to set
everything needed or you accidentally added duplicate constraint.
Button
can't be e.g. 0
from the left edge and at the
same time have a different constraint set to 20
also from the left
edge. Xcode will fairly well show you where's the problem and hints a fix
"with one click". I personally don't use these fixes because it often
ends up differently than what I wanted. I also almost never use Reset to
suggested constraints which sets the constraints for you. Maybe you'll be
comfortable with these options, just try them out. Remember that you will find
constraints in the Size inspector of individual components.
In the next lesson, Simple iOS calculator in Swift, we'll finally look at something more
real-world-ish and create a simple iOS calculator. We'll learn to use
StackView
and connect the UI with the code.