By introducing Auto Layout, Apple made a lot of progress in their attempt to help iOS developers in building their iOS app’s user interfaces. The Interface Builder got a lot better, the Auto Layout was integrated into it, and more recently UIStackViews are gaining a lot of traction and popularity. Having many different approaches of laying out views with Auto Layout, makes it necessary to decide upfront, on a consistent pattern to be followed when building your iOS app.
Laying out views in iOS apps might feel a little overwhelming for a newcomer who’s just getting started in the world of programming on Apple’s devices. Even software developers who are more experienced might have a hard time deciding which path should they pick when building their iOS apps. The reality is that all solutions have their trade-offs, and the answer is usually a function of the size of the team and the size of the expected codebase.
The user interface is clearly the most important part of an iOS app since that’s the only thing users interact with. In order to be successful, an app must provide an awesome user experience, which works well on all Apple devices and which adapts to all screen sizes. Adding to that the landscape support, we already notice how complex is the task of ensuring responsiveness in iOS apps.
In this article, we’ll shortly explain how each method (of laying out views) works and we’ll highlight potential trade-offs, based on advantages and disadvantages for each of these methods.
Auto Layout in Interface Builder
Probably the most common used out there, laying out views with this method is really simple and it only requires a minimum amount of effort. While the method itself is restrictive, most of the apps out there can have their UI implemented this way.
Auto Layout is a constraint-based layout system. iOS developers can create an adaptive UI, that behaves appropriately when there are changes in screen sizes and device orientations. More concretely, in Interface Builder, you can drag and drop your views and then add layout constraints on them. Based on these constraints, their layout is automatically set and adapted to any screen size and device orientation. The constraints are related to alignment (centering, docking, etc), sizes (setting width and height) and positions. You can use absolute values for each constraint, or even relative ones, by using multipliers. All of these can be done in the Interface Builder entirely. Check out Apple’s Auto Layout guide, for more details.
The advantages here are pretty clear:
– easy to implement
– fast to ship
– no code required
Unfortunately, using only the Interface Builder will prove restrictive as the app requirements grow, and it’s highly likely that you’ll also want to write some code, for more advanced and dynamic UI manipulation. The cool thing is that you can create outlets (IBOutlet) for any constraint in Interface Builder, so you can change their parameters in the code (constants, multipliers, etc). This obviously creates many more opportunities, and that’s the reason why you should really consider this method of laying out views.
There are two big disadvantages, though:
- merge conflicts are painful and hard to resolve – this will get out of hand as the team size scales up
- debugging layout issues is hard – identifying the bugs is time-consuming, and it gets even harder as the number of views grows
In conclusion, if you’re working with a small team and you know that the codebase won’t get too big in size, or if you just want to quickly deliver a working prototype of the app, this is the most suitable approach.
Programmatic Auto Layout
We can try and mitigate the disadvantages of the previous method, by getting rid of the Interface Builder altogether. The auto layout constraints can be created and configured programmatically. They’re essentially regular objects and can be manipulated like any other object. This eliminates the need of creating IBOutlets (for both constraints and views) and the merge conflict resolution problem on IB files disappears as well. Debugging also becomes easier, since the code is usually clearer and developers can use breakpoints.
On the downside, this method requires more boilerplate code and the UI elements can be visualized only when running the project. This is actually an acceptable trade-off for bigger teams, where the overall productivity is the main goal.
The huge deal-breaker here (which applies to the first method as well) is that whenever you make fundamental changes to your designs (like adding a container view for a bunch of views, adding/removing views positioned relatively to existing views, etc), there’s the need of breaking many existing constraints and recreating new ones, according to the new design.
Auto Layout with UIStackViews
This method provides a way to lay out a set of views, horizontally or vertically. Basically, you can specify how a view will adjust itself to the available space in its container, by configuring several simple properties (like alignment, distribution, spacing, etc). For more details about UIStackViews, see Apple’s documentation.
You will still have auto layout constraints, but their number is minimized, and if designed properly, your app won’t require too many changes when new UI design requirements come up. This method also works with Interface Builder, and you should really consider this approach if you expect that your app will go through many design iterations in the future (A/B testing, redesign, more advanced visual effects, many new features, etc).
This method is not mutually exclusive with the first two options above, and in case you don’t want just a simple prototype, you should definitely use stack views for your auto layout based iOS app.
If your iOS app doesn’t have a very exotic user interface, you can even use UIStackViews only, with no other auto layout constraints.
If you want 100% control on your layout, nobody is stopping you from computing everything manually. Laying out views with this approach consists basically of computing the rectangular frames for all of the views manually, based on the bounds/frames of their parent views. This is usually done in the layoutSubviews method, which is a called on all the views that are currently in the viewport, after every redraw loop.
This method scales well for big teams, but the individual productivity lowers a bit since there’s more (basic) math involved. On the bright side, debugging is (at least in theory) much easier.
This method is a legacy technique, so it’s not usually recommended. Big companies, that started their apps years ago, are still using it, given the fact that the cost of switching (basically rewriting everything) to the more modern mechanisms is high.
Another legacy approach is laying out views with auto resizing masks (also called “springs & sprouts” method). While sometimes they can be helpful, nobody really uses them these days, but it’s still good to be aware of their existence.