Easy Auto Layout constraint groups

If you’ve been making apps for a little while, there’s a good chance you’ve had to design a view with two different states. Say, an “expanded” state and a “compact” state. Here’s a simple example:

auto layout constraint

Doing this with Auto Layout (we all use Auto Layout now right?) can mean setting up a bunch of constraints for the primary state, a bunch of constraints for the secondary state, and then toggling them on and off as needed in your code (via the isActive property).

This is what I’ve done in the past for my apps and it’s worked fine… but it can lead to a lot of logic in your view controller that’s rather difficult to debug. And it can really start to get messy.

So, in the quest for ever-cleaner view controllers, I want to suggest a simpler approach. I was at RWDevCon this past weekend (recommended!) and watched Scott Berrevoets‘ talk on advanced Auto Layout, and it got me thinking: couldn’t state be embedded in the NSLayoutConstraints themselves? In other words, why can’t we assign each constraint to a particular group—in Interface Builder—and just toggle the active group with a single line of code?

Well, it turns out that this is relatively easy to implement, and I really like how it turns out. All it involves is adding the MemberConstraint class to your project (and an Array extension), setting up constraints in Interface Builder, and then adding a few lines of code. 👍 Here’s a video to take you through building the example at the top of this article (Xcode 8.3).

THE CODE

MemberConstraint.swift
import UIKit

@IBDesignable class MemberConstraint: NSLayoutConstraint {
 @IBInspectable var group:Int = 0
}

extension Array where Element == MemberConstraint {
 func activateState(_ state:Int, animate:Bool, view:UIView) {
  self.forEach{ $0.isActive = $0.group == state }

  if animate {
   UIView.animate(withDuration: 0.25){ view.layoutIfNeeded() }
  }
 }
}

Question or comments? Get in touch below, or come say hi on Twitter.

Leave a Reply

Your email address will not be published. Required fields are marked *