In a previous blog post I’ve talked about it a little bit, but today I want to dive deeper into how constraints are calculated. It sounds like a foreign topic if you don’t feel comfortable with auto-layout, because how can we calculate views, right?
Don’t worry I’ll walk you through every step in the process.
If you’re still unsure after this blog post however, you should definitely check out the auto-layout course. This will solve all the issues you might still have with constraints.
Attributes on views
First of, the views we have on our canvas have attributes. Those attributes are what give them a color, border, transparency, size, position, etc.
With those attributes, the look and feel of a view are defined. It’s what defines the appearance on screen.
If you’ve been working in the interface builder before, you know that most of those attributes are represented by numbers. Especially the ones that auto-layout can take control of. You can find them in the inspector view sidebar.
As you can read in here, the things that need to be defined, in order for a set of constraints to work properly, are the width, height, x- and y-position of a view. These, therefore, are the attributes that auto-layout can take control of.
Let’s look at the example from a previous weeks post.
View1.Width = 1* View2.Width - 1
If we solve this, it’s pretty obvious what will happen. We’ll take the width of View2 (View2.Width) and multiply it by 1, wich leaves us with the same value. Now we subtract 16 from the width. The result is the new width of View1. We have successfully solved a constraint.
In this example we are taking the width of a fictional View2 and define the width of our View1 using this information. That’s how we deal with constraints and as you can imagine, since we have 2 view objects we can chain as many different views together if we want to. This system is dynamically extendable. For example we could now set the width of a third view, based on the width of the first view, which is based on the second one and so on.
As you can see, we’ve been setting the width of a view right there. We’ve done that using math equations – constraints in this exact case. But what else could we do? Naturally, you can set the height like that. That’s the obvious one.
But for example, let’s focus on the position. That get’s a little more tricky. We don’t have a constraints that say x-position or y-position. Why is that you ask? That’s because these constraints don’t make any sense if you take your time and think about it.
Our Layouts are usually dynamic and not static. But what constraints like the ones we’ve looked at all have in common is that they’re all static. They don’t depend on screen size, or rotation. They don’t depend on the height that is available on screen but rather are set static.
That’s why there’s more constraints. For example we have constraints that can pin the left or right edge of a view to some other vertical edge. Same with top or bottom. Or we have constraints that align the views centered in a bigger view, or we can align edges that lie on top of each other, so that if one of the edges moves all the other ones move with it. For a bigger list of constraints, feel free to check out the auto-layout course. What I’m getting at however is this: There’s a lot more options than just setting a size and a position. This is why auto-layout is so flexible in use.
But these constraints can’t just be set to a constant number. They all depend on each other in most cases. This is why the constraints need adjusting as soon as something in the layout changes. The framework needs to detect if there are some changes made to the layout and needs to adjust accordingly.
Layouts are always nested in a bigger container view. Even the most simplistic ones. Imagine a blank canvas in the interface builder. If you look at it, there’s already a view in there. This is the main view of our apps screen. Inside of this screen there will be our layout.
Let’s think for a moment about when adjustments need to be done by the framework. Let’s say the app starts right now. You might think that there’s no adjustment necessary at that point because this is the initial state. The truth however is, that this is the first time the framework gets active and tries to create the initial layout. This is because there’s not one screen size out there. The software detects the dimensions of the screen the the device the app is running on has and adjusts the layout accordingly to set it’s initial layout.
Now let’s rotate the phone. Again the framework needs to do it’s job. It gets to work and starts arranging the views to adapt to the new screen dimensions.
On iPads this can have many more situations. For example on some iPads, apps can be viewed in sidebar view or in split-view mode. All of these scenarios are things that auto-layout takes care for us.
But how does the framework do that? Let’s look at an example.
We have a canvas – our apps screen – and 2 views inside of there. These two views together should take up the entire available space, but leave a margin of 8 around the edges. Also, they should both have the same width and height all the time. In the middle, the two views should not have a margin from 16 but also eight. This layout should always be maintained. The attributes that can be dynamic are the width and height of our views in this case. Here’s what I’m picturing.
Let’s look at it step by step:
- The framework asks for the screen dimensions. Let’s assume 200 width, 300 height.
- Views are displayed
- Autolayout starts getting to work and fills the entire space with the two views
- Introduce margin on the top
- Introduce margin on the bottom
- Introduce margin on the left
- Introduce margin on the right
- Introduce margin in the middle
- Set equal widths & heights
(Of course I can’t guarantee that this is the exact ordern in which auto-layout will solve this particular layout.)
Here’s what those constraints for the layout would look like:
redView.top = 1 * canvas.top + 8 blueView.top = 1 * canvas.top + 8 redView.bottom = 1 * canvas.bottom + 8 blueView.bottom = 1 * canvas.bottom + 8 redView.left = 1 * canvas.left + 8 blueView.right = 1 * canvas.right + 8 blueView.left = 1 * redView.right + 8 redView.width = 1 * blueView.width + 0 redView.height = 1 * blueView.height + 0
So if we look at the calculation step by step, the all so confusing auto-layout seems to be getting a lot easier. There’s no magic going on, it’s just simple math and logic doing their things. I know that the icons or the UI can be a bit counterintuitive at first. You’ll need a structural understanding of what auto-layout does first, but once that is done you’re good to go. Keep these things in mind the next time you’re sitting down to create a layout in interface builder.
I hope this helped you make this a little bit clearer. I know it is a confusing topic but you can learn it. It’s no magic and luckily, you don’t need a degree in thermodynamics to understand it. ☺️ However if you’re still feeling stuck with constraints, check out the auto-layout course! This course will help you master constraints with easy and will help you get rid of all your headaches about auto-layout.