SnapKit for iOS: Constraints in a Snap
In this tutorial you’ll learn about SnapKit, a lightweight DSL (domain-specific language) to make Auto Layout and constraints a breeze to work with. By Shai Mishali.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
SnapKit for iOS: Constraints in a Snap
20 mins
- Getting Started
- Snappin’ & Chainin’
- What is a DSL?
- SnapKit Basics
- Composability & Chaining
- Your First Constraints
- Do That Again
- A Quick Challenge!
- Final Constraint
- Modifying Constraints
- Updating a Constraint’s Constant
- Remaking Constraints
- Keeping a Reference
- When Things Go Wrong
- Where to Go From Here?
Keeping a Reference
While you won’t experiment with this option in SnappyQuiz, it’s still one you should know of.
In standard NSLayoutConstraint
fashion, you can store a reference to your constraint and modify it later on. That’s also possible with SnapKit, using the Constraint
type:
var topConstraint: Constraint?
lblTimer.snp.makeConstraints { make in
// Store your constraint
self.topConstraint = make.top.equalToSuperview().inset(16)
make.leading.trailing.bottom.equalToSuperView()
}
// Which you can later modify
self.topConstraint?.update(inset: 32)
// Or entirely deactivate
self.topConstraint?.deactivate()
When Things Go Wrong
Sometimes in life, things go wrong. This is even more often the case when talking about Auto Layout constraints.
Back in QuizViewController+Constraints.swift, find the following line:
make.centerX.equalToSuperview()
Right below it, but still inside the makeConstraints
closure, add:
make.centerY.equalToSuperview()
Build and run the app. As you can see, the UI is entirely broken:
Also, as expected, you’ll see a giant wall of broken constraints in your debug console, which should look similar to the following:
[LayoutConstraints] Unable to simultaneously satisfy constraints.
"<SnapKit.LayoutConstraint:0x600001b251a0@QuizViewController+Constraints.swift#62 UIView:0x7f9371e004a0.top == UILayoutGuide:0x60000062c0e0.top>",
"<SnapKit.LayoutConstraint:0x600001b25260@QuizViewController+Constraints.swift#64 UIView:0x7f9371e004a0.height == 32.0>",
"<SnapKit.LayoutConstraint:0x600001b2dc80@QuizViewController+Constraints.swift#38 UILabel:0x7f9371e088c0.height == 45.0>",
"<SnapKit.LayoutConstraint:0x600001b2dce0@QuizViewController+Constraints.swift#39 UILabel:0x7f9371e088c0.top == UIView:0x7f9371e004a0.bottom + 32.0>",
"<SnapKit.LayoutConstraint:0x600001b2dda0@QuizViewController+Constraints.swift#41 UILabel:0x7f9371e088c0.centerY == UIView:0x7f9371e09a50.centerY>",
"<NSLayoutConstraint:0x600001c6c2d0 'UIView-Encapsulated-Layout-Height' UIView:0x7f9371e09a50.height == 551 (active)>",
"<NSLayoutConstraint:0x600001c61450 'UIViewSafeAreaLayoutGuide-top' V:|-(0)-[UILayoutGuide:0x60000062c0e0'UIViewSafeAreaLayoutGuide'] (active, names: '|':UIView:0x7f9371e09a50 )>"
Will attempt to recover by breaking constraint
<SnapKit.LayoutConstraint:0x600001b2dc80@QuizViewController+Constraints.swift#38 UILabel:0x7fc53e41d060.height == 45.0>
Oh boy. Where do you even start? All you see is a bunch of memory addresses that don’t necessarily mean too much. It’s also quite difficult to understand which constraints were broken.
Luckily, SnapKit provides a great additional modifier to track down these sort of issues, called labeled(_:)
.
Replace the entire lblTimer
constraint block with the following:
lblTimer.snp.makeConstraints { make in
make.width.equalToSuperview().multipliedBy(0.45).labeled("timerWidth")
make.height.equalTo(45).labeled("timerHeight")
make.top.equalTo(viewProgress.snp.bottom).offset(32).labeled("timerTop")
make.centerX.equalToSuperview().labeled("timerCenterX")
make.centerY.equalToSuperview().labeled("timerCenterY")
}
Noticed the labeled(_:)
addition on every constraint? This lets you attach a descriptive title for every constraint, so you don’t have to pick through memory addresses and lose your sanity.
Build and run your app one final time. Your broken constraints should provide much clearer information at this point:
[LayoutConstraints] Unable to simultaneously satisfy constraints.
"<SnapKit.LayoutConstraint:0x60000365c4e0@QuizViewController+Constraints.swift#62 UIView:0x7fc53e4181d0.top == UILayoutGuide:0x600002b0ae60.top>",
"<SnapKit.LayoutConstraint:0x60000365e8e0@QuizViewController+Constraints.swift#64 UIView:0x7fc53e4181d0.height == 32.0>",
"<SnapKit.LayoutConstraint:timerCenterY@QuizViewController+Constraints.swift#41 UILabel:0x7fc53e41d060.centerY == UIView:0x7fc4fe507170.centerY>",
"<SnapKit.LayoutConstraint:timerHeight@QuizViewController+Constraints.swift#38 UILabel:0x7fc53e41d060.height == 45.0>",
"<SnapKit.LayoutConstraint:timerTop@QuizViewController+Constraints.swift#39 UILabel:0x7fc53e41d060.top == UIView:0x7fc53e4181d0.bottom + 32.0>",
"<NSLayoutConstraint:0x6000031346e0 'UIView-Encapsulated-Layout-Height' UIView:0x7fc4fe507170.height == 551 (active)>",
"<NSLayoutConstraint:0x600003139c70 'UIViewSafeAreaLayoutGuide-top' V:|-(0)-[UILayoutGuide:0x600002b0ae60'UIViewSafeAreaLayoutGuide'] (active, names: '|':UIView:0x7fc4fe507170 )>"
Will attempt to recover by breaking constraint
<SnapKit.LayoutConstraint:timerHeight@QuizViewController+Constraints.swift#38 UILabel:0x7fc53e41d060.height == 45.0>
This looks similar, but look carefully. You can see gems like timerCenterY
. This is much more informative, and you have some great labeled constraints to start debugging your way through.
More specifically, the only three labels you can recognize in this output are timerCenterY
, timerHeight
and timerTop
. Since the height is static, you can be sure the conflict is between the two constraints left. That narrowed things down much faster than picking through the original mess of Auto Layout debugging output!
Once you’re done, feel free to remove the centerY
constraint that started this mess.
Where to Go From Here?
Congratulations! You now know most of what SnapKit has to offer, but there are still a few features and modifiers you should look into, such as priority
, divided
and more. Check out SnapKit’s official GitHub repo for more information.
Remember, SnapKit is there to help you by creating an easy-to-consume, problem-specific syntax for creating constraints, but it doesn’t provide features that can’t be achieved with regular NSLayoutConstraint
s. Feel free to experiment with both and find a good middle ground that works for each scenario.
We hope you’ve enjoyed this tutorial. Got any more questions or … constraints ? Leave a comment in the forum thread below.