You now know what Constraints are and what they do, so we’re going to dive in a bit deeper how the constraints work in theory and how they are defined and where they can be „placed“.
There are basically two separate places you can use to create, set and work with constraints. First and foremost, there’s the Interface Builder. This is the obvious choice when you get started with constraints because it gives you a nice visual representation of what’s going on. But that’s not the only possible place to work with constraints, you can obviously also make use of them in code. We’ll take a look at that later in this section, as It helps to understand the basic theory in visual form first because the code gets more abstract and it helps to have seen and experienced the concept inside of the Interface Builder.
Now there is one thing left to note. There are different types of constraints and different places on a view that the constraints can effect. I’ll tell you exactly which constrains there are and which effect they can have, because it’s not exactly a mix and match type of thing. There are some rules that you need to follow in order to make this work, but don’t worry it is not as complicated as it sounds.
First of let’s get into the types of constraints.
Types of Constraints
Let’s just take a look at the most straightforward constraints that there are, which would be:
These constraints are really simple. Obviously, height and width define the size of the view. Top, bottom, left and right are there to define the position, based on the space that is measured from the top, bottom, left or right of the views „alignment rectangle“, which if you don’t know what that is, no worries we’ll get into that later in this lesson. In our case, the alignment rectangle is exactly the view itself.
But there’s more. Let’s take a look at another couple of constraints that you could set.
Now leading and trailing sound pretty straightforwardforward, but if you look back, we already have a left and right constraint, so the seem redundant. Well the difference is that the leading and training constraints define how the layout appears based on the fact if it’s a left-to-right language or a right-to-left language. So it’s especially important if you’re dealign with text. In most languages like english, german and so on, the leading edge is the left edge of the view and the trailing edge would be the right edge of the view, while as for example in hebrew or arabic type languages you would have both of those flipped, so the leading edge would be the right edge and the training edge would be the left edge.
That’s it to the leading and the trailing edges. Now we have another constraint that helps us to position our view more accurately. Those are the centers in X and in Y position. This is really handy because usually you’ll want to align content based on its edges or on its center. If you want to align something to the center, those are the constraints you’d want to use, because they act as if they were in the center of the view.
The next ones again focus on text. The direction is not important in this case but before I can explain what they do, we’ll need to have a super short typography lesson. What I’m going to tell you is what the baseline is. In our case right here you can already see what the baseline is. The baseline is the line on which we have all of our letters sit on.
This is super important because most of the time if you’re dealign with multi column layouts for text for example, you’d want the baselines to align to create a great reading experience. And there’s 2 ways to go about this, because there are two types of baseline constraints. The first one is the first baseline and the second one the second baseline.
Think of a multi line text field. The last baseline would be the baseline of the last line of text, while the first baseline would obviously be the baseline of the first line of text.
Now there are only a couple of constraint types left. Let’s look at those.
- Left/Right/Top/Bottom/Leading/Trailing Margin
- Center X/Y With Margins
If you’ve worked with css before, you most likely know what margin is. If you haven’t, no problem. Let me just explain. Imagine a UIView you just put in your project. I’ll reference this view as the „superview“. This UIView has a boundary around the edges of the view. Now if you want to nest a different view inside of this one, you most likely want to arrange it inside the view in some way.
If you’ve worked with interface builder before, you’ll have noticed that views that are nested inside of another view will snap to a helper grid. For example if you move one of the nested views in your superview to the edge, you’ll notice that it will snap close to the edge of the view, but not exactly to the outside boundary. There is a little bit of space. It basically gives the view a little bit of „space to breathe“ in the superview. That little bit of extra space is what is call margin.
Let’s just look at an example in Xcode to make this more tangible.
To create layouts that look great and don’t feel too cluttered, you’ll need this margin a lot. In the Interface builder, the margin is not as apparent as in code because you’ll work with it quite naturally, but still this is something you need to think about.
Don’t worry though if you’re unsure about the concept. We’re going to take a look at this again in the practice section here and there so that I can make sure you fully understand what this concept means and how it works.
So let’s talk about what the alignment rectangle is. I mentioned it before and now we’re going to dive into that.
If we talk about an alignment rectangle, we talk about the boundary that surround a view. It is essentially the boundary that snaps to the helper grid in the Interface Builder. It is apparent when dealign with a blank view inside of the Interface Builder because the alignment rectangle is pretty clear there.
But there are cases where the alignment rectangle in fact isn’t as apparent as when using a view. Imagine for example a label of text. The alignment rectangle is not directly visible, but it is still there and is still something you need to worry about.
Let’s just take a quick peek inside of Xcode to see what I’m talking about and then move on.
Anatomy of a Constraint
You now have a complete understanding of what types of constraints there are in the auto-layout framework. But that’s not all there is. What I haven’t told you yet, is what exactly a constraint looks like.
I told you before that – under the hood – constraints are math equations. But right now we don’t see a lot of that. Actually we have never seen any type of equation in this sense at all. So, let’s fix this.
Constraints are equations. What do equations need? They need parameters. Just like most functions, there is a set of varying numbers that define the equation. In code these parameters are passed with the use of function arguments. But right now let’s go the little more abstract route, because we can visualize the equation in a way that is much easier to understand.
Apple hast a great way of visualizing the „Anatomy of a Constraint“ in it’s documentation, and I’m going to use this example to explain what’s going on.
Let’s take an example. The constraint should look like this.
What you can see here, is exactly how constraints are calculated. Now because this can be a little overwhelming at first, let me break it down for you.
Firstly there is to note that each Constraint is usually a relation between two views. A constraint can however exist on just a single view as well. Most of the time though, a constraint is an interaction between two different views and how they behave. So the equation features two view objects. The red view and the blue view in our example right here. Those are the views that we want to create a behavior between.
Also we have two attributes. Those are the attributes of the views that we’ll use to adjust the views according to the superview. For example you’ll need to specify, which places on the view the constraint need to be applied to. In our case here this is the left and the right boundary of the corresponding views alignment rectangle. So the redView.left which means we take the left edge of the red view and the blueView.right because we want to take the right edge of the blue view.
If you now set the constraint to the left side of a view, for example the red one, it usually is equal with the constraint on the right side of the view on the left, our blue one in this case. This relation is represented by the equals operator in the equation. There are also other types of relations like more-or-equal as well as less-or-equal, however I’ll get to those in one of the examples, because this si a little more difficult to explain.
Multiplier and Constant
The last thing we need in this equation are the multiplier and the constant. The Constant is very straightforward. This is a value that just gets added to the value of the constraint. For example if our views in this case here would align perfectly on the sides, that would mean there would be no gap between them. In order to introduce a gap of 8 points between the two views, we would enter a constant of 8 here.
Another example: Let’s assume that the blue view is bound to the left margin of the view it is nested inside, we would already have a margin of 8 if the left margin of the superview is 8. However this would not be reflected inside of our sintraint because we are using the margin of the blueView. If we wanted to increase the gap to 16 on the left side, we could use the constant and add 8 which would result in a sum of 16, or we could use the multiplier and multiply the value of the superviews left margin by 2. Our constant would remain 0 in this case.
Combination Of Constraints
Like I noted before, a proper set of constraints is one that has one and only one possible solution. For that to be possible we have to think about how to arrange the types of constraints in a way that leaves us with only one possible solution. This is easier to understand if we look at the attributes of the view that can be adjusted by a constraint.
All that a constraint can do is move the view around and change its size. That’s essentially all the information it needs to work.
So a proper set of constraints sets:
- the size
- and the position
The only restriction in this case is that there can’t be constraints that conflict each other. But we’ll take a look at that later.
Permutations of Constraints
Lets focus on the ways that Constraints can work together. This is the part where it might get a little confusing, but don’t panic, I have some examples prepared that can illustrate all of this very well.
The thing is, you don’t necessarily have set the width and height constraint to define the width and height of a view. You can go about defining the size in different ways. For example:
Let’s say you want a square centered on your canvas. Admittedly, this is not a very creative layout, but it serves a good purpose so I’ll just run with it.
You can use the width and height constraint and set them both to the same value. This would create a square. Now we only need to specify the center-x and center-y constraints to match with the center-x and center-y constraints of the superview and we’re done.
Now, this layout matches all the criteria to be a valid set of constraints. The width, height, x- and y-position are all set and therefore the layout can be solved. But this is only one possible example.
Shrinking A Rectangle
Another example would be this:
Again we have a view inside of a superview. We could now bind the left edge of the view to the left margin of the superview. Then bind the right edge of the view to the right margin of the superview, and the same with the bottom and the top. This would leave us with a view nested inside a superview with just a little border between the two views.
Let’s check if this would work. Is the width set? Well, yes it is. If we know the size of our superview, the width can easily be calculated. So the width is the size of the (superview – margin on the left) – margin on the right. The calculation of the height works exactly the same way. Now more interestingly, is the x- and y-position set? Again, the answer is yes. Both are set, due to the fact that the edges of the view are bound to the margin of the superview. There is only one possible solution to this layout.
See how this works? There are multiple ways to go about this. For example you could bind a left and right edge of a view to a superview and have the width and x-position set, and specify the height and y-position using the height and y-position constraints.
There are many permutations of this and as you can imagine this can get confusing at times. The only thing I’d recommend here to remember is that all the parameters – width, height, X-Position, Y-Position – need to be set in order to have a valid set of constraints.
So now that we’ve got that sorted out, let’s move to the last important part of constraints. The last thing that can influence constraints are priorities. This is pretty easy to explain, because priorities are numbers ranging from 1 up to 1.000, with 1.000 being a required constraint (meaning that the app will crash if the constraint can’t be resolved) and 1 to 999 being different levels of priority for resolving. This is interesting for example if we have two conflicting constraints. If both of the constraints have the priority of 1.000 (required), the app will crash. If one of them has a lower priority, the other constraint will be executed correctly and then the one with the lower priority will be ignored. If we have two conflicting constraints with less than 1.000 priority, the constraint system will execute the first one as good as it can and then move on to the next one. This means that if the constraint can’t be met 100% the app will not crash and the constraint system will try to get as close as possible and then move on.
So, this concludes the theory part of the auto-layout module. I know this is a weird and complex seeming concept at first, but to be honest I can not imagine having a more flexible and intuitive way to handle layouts than this. Once you wrap your head around this, it becomes second nature to you and you’ll never want to go back to any other system, I promise you!
Next we’re going to dive into some practice and apply this knowledge to an example. But before we put all of this constraint knowledge this into action, let’s just look at a way to create layout without the use of constraints to get used to the interface builder.