When I first started coding, I was 100% on team StoryBoard. It was easy to see how different UIViews interacted, and you could place them exactly where you wanted. This worked for a single screen size, but as the Views became more complex and needed to accommodate various screen sizes, the hierarchy of constraints became more and more difficult to tie down.
Still, programmatic was scary! Swift’s built-in methods are the stuff of nightmares. And even though I am now fully onboard with designing apps programmatically, just looking at those methods still makes me recoil in fear. Luckily, to the relief of many-a-coder, the blessed souls at SnapKit have designed an insanely simple auto layout DSL for iOS and OS X written entirely in Swift!
To get started, simply install the SnapKit CocoaPod using pod 'SnapKit'
in your podfile, and open your project’s .xcworkspace. Designing apps programmatically takes a little bit of set up. First go to your app’s General Settings, and under Deployment Info’s Main Interface, delete where it says Main
. This tells your app not to worry about Storyboards anymore!
Still, the app needs a point of entry… This is where the AppDelegate comes into play. There you want to declare an instance of your first ViewController. (If you are using a UINavigationController, UITabBarController, or ContainerViewController, this is also where you would set those up, but for now we will work with a single ViewController). In the AppDelegate’s func application(... didFinishLaunchingWithOptions...)
method, initialize the window
as a UIWindow()
with a frame equal to your UIScreen’s bound, and initialize your primary ViewController. Next set the window?.rootViewController
equal to your newly created ViewController, and make the window
visible!
Five lines of code and your application will open to the ViewController you specified. Now for the fun stuff, let’s make some views!
First step is to, of course, import SnapKit. (If it claims no such module exists, give it the old Command + B; also check you are working in an xcworkspace and not just the xcodeproj). Usually the first UIView I make takes up the whole screen, so let’s create an instance of a mainView: UIView!
, initialize it, and add it to the ViewController’s view
setting it equal to the size of the screen.
Within the mainView.snp.makeContraints
method, each time you write $0
it simply refers back to the constrained -view itself! Setting the edges equalToSuperview will allow this View to expand as much as its superView (the UIScreen’s view
) will allow. This is an easy introduction, but let’s see what other options exist within snp.makeConstraints
.
Now that we have our background View, let’s add a header where we may want to display some general info. This new View will be added on top of the mainView
, and will be proportionate to the height of the Screen/SuperView:
Notice how you can chain together different constraints to the SuperView and add a new line if the property varies. This makes the code extremely easy to both type and read!
At this point we have a mainView the size of the UIScreen colored cyan, and a headerView colored blur which covers the top 25% of the Screen. Let’s add a Label and have it centered directly within the headerView
:
With UILabels, you do not have to set them to a specific height or width; that will be determined more so by the text and font/size you have selected. As such, you can simply give it an X/Y axis position and Xcode will presume the rest.
The final result should show a screen 3/4 Cyan and 1/4 Blue with a Label placed directly center with the blue section.
There are plenty of other methods available in SnapKit, most of which are entirely self-explanatory. For instance if we wanted the mainLabel
to be in the center of the screen, but somewhat below the blue headerView
, you would remove the centerY
constraint and add a new line indicating $0.top.equalTo(mainHeader.snp.bottom).offset(10)
. This will pin it below the mainView
but slightly below.
As you can see, SnapKit is great for compartmentalizing your views into different sections of subViews, similarly to have Xibs behave. I usually put all my constraints into a single method and call it after all views have been initialized. This helps make the already readable code easy to locate and test for.
I encourage you to explore and play around with the bundle of methods SnapKit to get a feel for how simple these constraints are to implement in comparison to the large blocks of text you would use otherwise. The one word of advise I have is that, like many aspects of code, the commands are linked sequentially, meaning if you set a constraint to a target that hasn’t been fully initialized or constrained itself, your app may crash. Fortunately SnapKit includes helpful print statements notifying you of what may have caused the crash!
I hope this blog helps alleviate some of the fear associated with programmatic layout and encourages you to make use of a fantastic and well-maintained repository! In my next blogpost I will show you how to use these programmatic constraints to create an imitation-UICollectionView using only UIStackViews. Until then, happy coding!