The whole UI of the app, I helped to develop, is written in code. No Storyboards, no XIBs, just Swift. Someone made this decision looooong ago. Nevertheless, we're using AutoLayout, just like every cool kid should do these days.
Typically, our code looks like this:
self.titleLabel.topAnchor.constraint(equalTo: self.titleView.opAnchor, constant: 16).isActive = true
Now imagine, that there's more than one constraint, take this for example:
contentWrapper.leftAnchor.constraint(equalTo: swipeContentView.leftAnchor, constant: 8).isActive = true contentWrapper.rightAnchor.constraint(equalTo: swipeContentView.rightAnchor, constant: -8).isActive = true contentWrapper.topAnchor.constraint(equalTo: swipeContentView.topAnchor, constant: 4).isActive = true contentWrapper.bottomAnchor.constraint(equalTo: swipeContentView.bottomAnchor, constant: -4).isActive = true imageView.topAnchor.constraint(equalTo: contentWrapper.topAnchor, constant: 12).isActive = true imageView.leftAnchor.constraint(equalTo: contentWrapper.leftAnchor, constant: 12).isActive = true imageView.widthAnchor.constraint(equalToConstant: 120).isActive = true imageView.heightAnchor.constraint(equalToConstant: 90).isActive = true titleContainerStackView.leftAnchor.constraint(equalTo: imageView.rightAnchor, constant: 12).isActive = true titleContainerStackView.topAnchor.constraint(equalTo: contentWrapper.topAnchor, constant: 12).isActive = true titleContainerStackView.rightAnchor.constraint(equalTo: contentWrapper.rightAnchor, constant: -12).isActive = true stepper.translatesAutoresizingMaskIntoConstraints = false stepper.leftAnchor.constraint(equalTo: contentWrapper.leftAnchor, constant: 12).isActive = true stepper.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 9).isActive = true stepper.widthAnchor.constraint(equalToConstant: 120).isActive = true stepper.heightAnchor.constraint(equalToConstant: 32).isActive = true shortDescriptionLabel.topAnchor.constraint(equalTo: titleContainerStackView.bottomAnchor, constant: 4).isActive = true shortDescriptionLabel.leftAnchor.constraint(equalTo: imageView.rightAnchor, constant: 12).isActive = true shortDescriptionLabel.rightAnchor.constraint(equalTo:contentWrapper.rightAnchor, constant: -12).isActive = true bottomStackView.bottomAnchor.constraint(equalTo: contentWrapper.bottomAnchor, constant: 0).isActive = true bottomStackView.leftAnchor.constraint(equalTo: contentWrapper.leftAnchor, constant: 12).isActive = true bottomStackView.rightAnchor.constraint(equalTo: contentWrapper.rightAnchor, constant: -12).isActive = true
I see two issues with this approach:
- Although it might be very comfortable for you, while you write this code, to do two things at a time — creating a constraint and activating them — it's something, your future-self will hate you for.
- There's the risk of forgetting to add a
.isActive = true, at least this happened to me a few minutes ago.
There's a better way than activating each constraint individually: It's called
This convenience method provides an easy way to activate a set of constraints with one call. The effect of this method is the same as setting the isActive property of each constraint to true. Typically, using this method is more efficient than activating each constraint individually. — Source
The last couple of hours, I've been working on a screen, that features two
UILabel and a
UICollectionView. In the end it should look similar to this:
So I added all the elements and constraints and pressed
CMD+R. But it didn't work. The numbers weren't visible, but at the same time, there were no unsatisfiable constraints. I doublechecked the constraints, and they looked fine. After some time, I found out, that there was a
isActive = true missing.
After fixing this and
CMD+R-ing again, I applied a little refactoring using
NSLayoutConstraint.activate() right away. Now this code looks similar to this:
let constraints = [ self.titleLabel.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 16), self.titleLabel.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 16), self.view.trailingAnchor.constraint(equalTo: self.titleLabel.trailingAnchor, constant: 16), // ... ] NSLayoutConstraint.activate(constraints)
Much better. If you were very strict, you could argue, that I should put each constraint into a constant before adding it to the
I'm pretty sure, that using
NSLayoutConstraint.activate() in the first place would have saved me some time. When I will add some more constraints in the future, I don't have to think about activating them. If I forgot the
,, the compiler will complain. Future-Nathan will be thankful for this.