isActive = true vs. NSLayoutConstraint.activate
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 NSLayoutConstraint.activate()
:
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 constraints
-array.
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.
Hopefully.