UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

The 5 best alternatives to Auto Layout

When Auto Layout works it’s magical. When it doesn’t… well, let’s just say you might want one of these.

Paul Hudson       @twostraws

If you’re tired of fighting with Auto Layout, you’re not alone. Even though it’s a remarkable piece of software, Auto Layout can at times be baffling, overly complicated, and perhaps even downright obtuse.

Don’t worry, though, because help is at hand: there are lots of wrappers around Auto Layout that can help to make your layout code easier or more expressive. So, in this article I’ve picked out some you might want to consider and given examples of each so you can compare for yourself.

Note: Because each of these is more or less the same thing – a domain-specific language for Auto Layout - I’m going to get right to the code examples, because that’s what makes them stand out from each other.

TAKE YOUR SKILLS TO THE NEXT LEVEL If you like Hacking with Swift, you'll love Hacking with Swift+ – it's my premium service where you can learn advanced Swift and SwiftUI, functional programming, algorithms, and more. Plus it comes with stacks of benefits, including monthly live streams, downloadable projects, a 20% discount on all books, and free gifts!

Find out more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Cartography

GitHub link and documentation

Cartography uses a handful of global functions to let you constrain, align, and distribute views using closures. For example, you set the width and height of a view like this:

constrain(view1) {
    $0.width  == 100
    $0.height == 100
}

That constrain() function comes with alternatives that handle up to five views at a time, so if you wanted to configure multiple views with the same size you could write this:

constrain(view1, view2, view3) {
    $0.width  == 100
    $0.height == 100
    $1.width  == 100
    $1.height == 100
    $2.width  == 100
    $2.height == 100
}

For times you need more than five you can pass an array instead, and when you do so you’ll be given an array of things to configure inside your closure, like this:

constrain([view1, view2, view3]) {
    $0.forEach {
        $0.width == 100
        $0.height == 100
    }
}

Cartography uses the operator ~ to enable layout priorities, for example $0.width == 100 ~ 500.

Alongside constrain() there are also the align() and distribute() functions to align views and space them evenly. For example, we could use constrain() and align() to align two views so they have the same leading edge:

constrain(view1, view2) {
    align(leading: $0, $1)
}

Using distribute() you can ask for views to be spaced out horizontally or vertically, using a fixed amount of spacing between each of them:

constrain(view1, view2, view3) {
    distribute(by: 10, horizontally: $0, $1, $2)
}

EasyPeasy

GitHub link and documentation

EasyPeasy uses one operator, <-, a collection of custom data types, plus some method swizzling and extensions to produce some syntax that works surprisingly well once you’re over the initial shock.

Mimicking the example from Cartography above, we can set the width and height of a view like this:

view1 <- [
    Width(100),
    Height(100)
]

That can be expressed more precisely using Size, which combines both Width and Height, like this:

view1 <- [
    Size(100)
]

For handling priorities, EasyPeasy groups them into low (equivalent to priority 0 in Auto Layout), medium (500), high (750), required (1000), and custom, where the last one lets you specify a precise Auto Layout number of your choosing.

You can then attach these to individual constraints like this:

view1 <- [
    Width(100).with(.high),
    Height(100)
]

Alternatively, you can attach them to the whole array of constraints, like this:

view1 <- [
    Width(100),
    Height(100)
].with(.high)

Arguably the most powerful feature of EasyPeasy is its ability to store a variety of layouts depending on layout conditions – you literally give it all the layouts you might want, and let it select which is correct.

For example, you might want a view to be 100 points in size and centered when using the horizontal regular size class, but pinned to the left and right edges in the compact size class. Here’s that using EasyPeasy:

view1 <- [
    Size(100),
    Center()
].when { $0.isHorizontalRegular }

view1 <- [
    Height(100),
    Leading(0),
    Trailing(0)
].when { $0.isHorizontalCompact }

Before I’m finished I want to show you one highlight:

view1 <- Edges(10)

That one tiny, concise line of code is enough to make view1 fill its superview with 10 points of spacing on all sides.

SnapKit

GitHub link and documentation

SnapKit adds a snp property to your views that can be used to add, remove, and adjust constraints quickly and easily. Continuing our example, we can make a view that is precisely 100 points in size like this:

view1.snp.makeConstraints {
    $0.width.equalTo(100)
    $0.height.equalTo(100)
}

You can put that on one line if you want, either like this:

view1.snp.makeConstraints {
    $0.size.equalTo(100)
}

Or even this:

view1.snp.makeConstraints {
    $0.width.height.equalTo(100)
}

Helpfully, what you pass into equalTo() can be a number or a different view. So, we could ask view1 to snap to the edges of its our main view like this:

box.snp.makeConstraints {
    $0.edges.equalTo(view)
} 

You could even add some edge insets if you wanted:

box.snp.makeConstraints {
    $0.edges.equalTo(view).inset(10)
}

That doesn’t have the pithy conciseness of EasyPeasy, but I’d say it was more readable at first glance.

This method of chaining extends to priorities as well, and you can end any of your constraints with a .priority() call either using one of the built-in presets:

$0.edges.equalTo(view).inset(10).priority(.medium)

Or using a free-form number:

$0.edges.equalTo(view).inset(10).priority(556)

Stevia

GitHub link and documentation

Stevia is probably the most flexible of all the options here, offering three different ways of laying out your views – one of which is a visual formatting language that lets you configure both the X and Y layout of your views in a single pass.

Let’s start simple: before you do anything you need to call view.sv() on your views, which is a shortcut to disable the automatic translation of autoresizing masks and adding a subview to another view. So, you’d write something like this:

view.sv(view1)

Once that’s done you can set the width and height of view1 like this:

view1.width(100)
view1.height(100)

As with some of the other options here, those calls are chainable:

view1.width(100).height(100)

They can also be wrapped in a single call:

view1.size(100)

What gives Stevia an advantage here is that these sizing methods have awesome percentage-based alternatives that make constraints really natural. For example, we could tell view1 that it should be half the size of its parent and centered in just two lines of code:

view1.size(50%)
view1.centerInContainer()

You have to admit, that’s remarkably concise!

You can make a single view fill all available space in its parent using its fillContainer() method, which has an optional padding parameter. For example, we could tell view1 to fill the screen with 10 points of spacing around all edges like this:

view1.fillContainer(10)

There’s also a neat bonus method called heightEqualsWidth() that adds an aspect ratio constraint.

And now for something that really sets Stevia apart: it has a visual layout language that lets you create both horizontal and vertical constraints in a single pass.

VFL is a real love/hate topic in Auto Layout because it can start to look like line noise after a while, and Stevia’s equivalent is no different. This, for example, will lay out our screen so that view1 has 50 points of spacing from the left and right edges, 100 points of spacing from the top, and is 200 points high:

view.layout (
    100,
    |-50-view1-50-| ~ 200
)

While I’m not a big fan of VFL, it’s great to see Stevia providing the option, and it’s even better to see they’ve made it significantly safer by removing the usage of strings.

Although it’s a shame there’s no Stevia API for priorities (all constraints are created as priority 751), it does deserve bonus praise for its comprehensive documentation.

TinyConstraints

GitHub link (no documentation)

As the name suggests, Tiny Constraints is a relatively simple Auto Layout wrapper that makes a selection of tasks very quick. Just like other options here you can use it set width and height constraints:

view1.width(100)
view1.height(100) 

You can make a view go edge to edge inside its parent like this:

view1.edges(to: view)

And there’s an option that allows you to specify padding on each side, but it uses UIEdgeInsets so it’s a bit unwieldy:

view1.edges(to: view, insets: UIEdgeInsets(top: 10, left: 10, bottom: -10, right: -10))

Notice the negative insets on the bottom and right sides; that doesn’t feel terribly intuitive to me.

As for priorities, TinyConstraints gives you four: fittingSize has a value of 50, low has a value of 250, high 750, and required 1000. They are easy enough to use because you can add them to the other method calls above, like this:

view1.height(100, priority: .low)
view1.width(100, priority: .high)

There are a few bonus methods for positioning various anchors, for example view1.center(in: view) will center the view, or view1.bottom(to: view) to place it at the bottom of the screen, but that’s about it – tiny, like the name says.

Which would I recommend?

I spent about an hour with each of the options on this list, trying to make simple layouts and trying to make more advanced layouts too.

I hope it’s pretty clear to you that each have their own advantages: Cartography probably has the fewest surprises, EasyPeasy is extraordinarily concise, SnapKit has extremely clear syntax that makes it easy to read, Stevia is ridiculously powerful, and TinyConstraints really lives up to its name – it’s tiny.

As well as each fitting different uses, it’s important to say that this is a deeply personal choice: some people love Perl-like code brevity, whereas others prefer more verbose method and parameter names that help make their code self-documenting. While writing this article I really fell in love with EasyPeasy – the syntax is quite alien at first, but it’s worth the work just to get the excellent size class functionality.

With those provisos out of the way, if I were to choose an Auto Layout wrapper today I would go for Stevia. Honestly, it wasn’t one I had used before I put together this list, but I was hugely impressed by its extraordinary flexibility: being able to switch between specific amounts, percentages, and relative layouts freely is quite liberating, and its selection of other helper methods provides a powerful arsenal to tackle even the gnarliest of layout needs.

I also felt that Stevia offers a sensible blend of concise code and readable code, which ultimately gave it the edge over EasyPeasy. Yes, EasyPeasy’s size class system is impressive, but its syntax is quite unlike anything seen in Apple’s own frameworks.

So, Stevia is a worthy winner of a Hacking with Swift Recommended award, and I encourage you to give it a try – it could well be the beginning of the end for your Auto Layout problems!

Bonus: does UIStackView do what you need?

If you don’t fancy learning a whole new framework just to make your layouts easier, keep in mind that ever since iOS 9 Apple has been recommending you start your layouts with a UIStackView.

Stack views aren’t like regular views at all – you can’t give them a background color, for instance – and instead are solely there to help control your layouts. If you haven’t used them before, I made a video just for you…

Double bonus: debug Auto Layout problems visually

When you have an issue with your Auto Layout constraints, Xcode spitting out a hundred lines of log messages to try to “help” is probably not quite as useful as Apple intended.

Fortunately, John Patrick Morgan has built a site that can help transform Xcode’s logs into simple, beautiful visual representations: WTF Auto Layout, which (before you imagine otherwise!) stands for “Why The Failure?”

Give it a try next time you hit a constraints problem: https://www.wtfautolayout.com.

BUILD THE ULTIMATE PORTFOLIO APP Most Swift tutorials help you solve one specific problem, but in my Ultimate Portfolio App series I show you how to get all the best practices into a single app: architecture, testing, performance, accessibility, localization, project organization, and so much more, all while building a SwiftUI app that works on iOS, macOS and watchOS.

Get it on Hacking with Swift+

Sponsor Hacking with Swift and reach the world's largest Swift community!

BUY OUR BOOKS
Buy Pro Swift Buy Pro SwiftUI Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.9/5

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.