Are you ready for another example for auto-layout? Open up Xcode and create a new plain project, this is going to be fun!
In this post, we’re going to take a look at the differences between fixed and dynamic sizing and positioning. I’ll tell you all about different approaches to creating layouts and making things work the way you want them to!
We’re going to start from scratch and create this simple example. It doesn’t look like much, but for our purposes today, this is great. I don’t want to overwhelm you and that’s why we will be dealing with a single view this time. But don’t think this can’t get interesting! I promise it will!
Let’s get to work!
Application specific behavior
The thing I was most confused about when starting with auto-layout, was the fact that I had to design behaviors for views. That sounded so odd to me! I mean, it’s pretty clear how you could design a views size, position, color, border, alpha and many more things. After all, those are just numbers. But how can we define a behavior? Sounds super weird and complex.
Well, it actually is quite simple. But before we can get started with designing and implementing a behavior, we first need to make sure the behavior is well defined.
Let’s start with our example.
As you can clearly see, we have a view in the middle of a canvas. Nothing fancy right? But if you think about it more in detail it’s actually not that trivial. How do we make sure that the view stays centered throughout all devices? And also, what if we want to increase the size of the view on an iPad but we want to keep it smaller on an iPhone?
We covered a couple of the things we’re going to use this time in a previous post already, but this time we’re introducing some different concepts. We have a different purpose for our view that is in here and therefore it might need different behavior than the last example did. Every application has a specific behavior and in order to be able to create all of them, you need to know about them and how to create them!
All of these questions will be answered today! So let’s switch to Xcode!
Fixed or dynamic positioning and sizing
First of we’ll take a look at the differences of fixed and dynamic positioning. Let’s start by dropping in a plain view. This is the only one we’ll need for this example.
Let’s also give it a color. I used #398EEA in this case.
We’re going to position it and give it a fixed size for now. We’ll do that by checking the top and left pin item with a constant of 0 in there. (if you don’t know what those do, feel free to check out this previous post). Also, we’ll check the width and height constraint. We’ll be deleting them later but for now, just roll with it. We need them so that auto-layout can work for us.
That’s basically all there is to fixed sizing (width and height) and positioning (pinning). We can use this method to make sure a view always stays at the same place on the screen and it keeps the same size. Of course, you could also do this using the pin to right edge constraint or you could pin it to the bottom instead of the top.
As you can see fixed sizing and positioning is not really difficult. But if we wanted to achieve that, we could have just let the constraints out right? because the behavior we just created is exactly what happens if you have no constraints at all. That’s true, but it makes a huge difference if, for example, you pin this to the bottom edge instead of the top one. Try it out if you like! It’ll definitely help you understand what you’re doing.
But still, this is static positioning. Let’s take a look at what we need to do if we want to center a view.
First of, we need to remove the constraints that keep our view fixed. Let’s remove the pin constraints for now and replace them. We can do that by selecting them in the outline view on the left side and hitting the backspace key.
Next, open up the „Add New Alignment Constraints“ dialog and select „Center Horizontally In Container“ and „Center Vertically In Container“. Now add those constraints.
After you click add on the dialog, the view you should end up with should look like this:
Perfect! Now we’re done, right? Looks like the image we wanted to get! We’ve got the width and height fixed to a certain value and the positioning is dynamical. Looks like we’ve done everything right!
Well yes and no. On the one hand, the image is exactly like the one I introduced to you in the beginning, but there’s more that we can do to make this work on iPads for example. Also, I want to show you a couple of other nice things that you can make use of to make this screen adapt to almost any screen size!
Sounds good? Okay, let’s do it!
First of, I don’t like the fact that we have a view that is defined by width and height. Imagine if this was an image view and you would try to scale the image up. You would need to figure out which width you want, and then set this value to the width and the height manually so that the image doesn’t get deformed.
What If we could make sure the aspect ratio of the image is always kept? For example, if we have an image that is two times as high as wide, we would just need to set the width and the width time 2 would be applied to the height. That would be awesome. This would make sure that the image always looks its best, no matter what the size.
Luckily, there is a way to achieve that and it requires just a single click!
Let’s first make sure that we delete that height constraint that we set. From now on, we want the size of the image (width and height) to only be controlled by the width. The height should scale according to the width. So select and delete that height constraint!
Next, we’re going to add an „Aspect Ratio“ constraint to the view by using the „Add New Constraint“ dialog. For this to work properly, we need to make sure that the view already has the aspect ratio it should keep. If this is the case, open the dialog and select „Aspect Ratio“ and click „Add Constraint“.
Defining width with pinning
As the next thing, we want to make use of the pin constraints to define the width (and now also height) of the blue view. We’re going to do that by pinning the left edge of the blue vie to the left edge of the container and the same with the right. Like so:
Finally, we need to remove the width constraint of the blue view as well, because otherwise, it will conflict with the two pin constraints that we just added.
If you now switch to another device in the interface builder, you can see that our width of our view now scales with the device itself. How awesome is that, right?
Now let’s assume this is a splash screen for your app and the view we have in here is a logo view. That means we have an image in there that will be displayed. However, images have resolutions and you can’t just scale them indefinitely. There’s a limit to the scaling you can do to an image until it looks pixelated.
We might need to consider limiting the scaling that the image can handle. Maybe we know that the image can’t be scaled bigger than 350pt. 350px is too big to be displayed on an iPhone, so on the iPhones, we would need to scale the image down to avoid getting cut off at the sides. On the other hand, we would need the image to scale to full 350pt if possible, so that we can use the full resolution o the image and it doesn’t look too small.
We can achieve this behavior using inequalities. Now, this sounds confusing again, I know, but let me assure you it’s not nearly as difficult as it sounds!
First, let’s think about, which constraints are causing this massive scaling. Since we don’t have a width constraint, the image width (and also height) is defined by the pinnings to the side. We need to allow them to grow bigger if the image reaches a certain size (350pt in our case). That’s already all the information we need! We now know that our pinnings to the side need to be able to grow bigger than the size they have right now if they need to, and we need to define the maximum size the blue view (or later the logo) can handle.
Let’s start by adjusting the pinnings on the side. They need to be able to grow bigger. So we need an inequality of „greater-or-equal“. Find the left and right pinning constraints in the size inspector
and change the ration to „greater-or-equal“.
Once that is done, the auto-layout system will intervene. There’s a problem. It’s the fact that the system doesn’t know when the inequalities should start to grow bigger because the blue view doesn’t have a maximum width.
We can easily fix that by adding a width constraint again.
Once this constraint is in, we need to go ahead and tell the auto-layout system that this is the maximum width the view should ever reach. My first thought would be to switch to a „smaller-or-equal“ relation. But that won’t work because the framework can’t really decide wether it should grow the view first or try to grow the pinning constraints first if there is more space available.
That’s why we should set the priority of the width of the view to 999 instead of changing the relation. We can do that in the same dialog where we also changed the relation.
And we’re almost done! Let’s just adjust the width constraint to the 350pt we discussed earlier, and off we go!
The task is now done. We’ve successfully developed a layout with inequalities and we even used a priority.
Of course, I didn’t touch on all the things that there are to note because it can get confusing at times.
I hope this post was helpful to you! If this wasn’t enough information for you and you’re eager to learn more about the topic, feel free to check out the auto-layout module. The module is a course that I’ve developed to help you solve any layout problems in the interface builder and in code! I’ve spent months creating this module and I can confidently say that it’s my best work as of today. I hope you check it out and that it will help you in the best way possible.