Autolayout with Swift 4's KeyPath

Along with JSON it looks like creating a new layout engine or wrapper is the biggest iOS industry, so why don't I do everyone a favour and add to the ever increasing bucket of unnecessary layout libraries?

Writing auto layout in code has gone through a few iterations. First, in iOS 6, came the NSLayoutConstraint initialiser: init(item:attribute:relatedBy:toItem:attribute:multiplier:constant:)Yeah, just bask in that glow.

This, don't forget, also came with visual layout and the wonderful constraints(withVisualFormat:options:metrics:views:). Mmmmmmmm.

Skip forward to iOS 9 and we got anchors. Things were looking much better. Thank you constraint(equalTo:). Except, I don't know about you, but two things always bothered me.

  1. It was still a little boilerplatey
  2. I always (seriously... always!) forgot to set isActive to true.

To expand on 1) the issue I had was if I was matching the topAnchor of two views then I'd have to repeat the anchor.

view.topAnchor.constraint(equalTo: otherView.topAnchor)

Oh, I forgot isActive again...

In Swift 4 we now have the ability to refer to uninvoked references to properties. Basically, just the property of a type instead of the value of the property for any given instance of that type. What I would like it to see if I can use key paths, basically so I can say "set these two view's topAnchor to be the same". And now we can, because we can refer to the topAnchor as a property, not the property's value. In code we're write it like this:

subview.anchor(\UIView.topAnchor, to: view)

So what does that method look like? It turns out to be quite simple

Ok, it's not a real blog post until I take it too far. So what can I do now? Well, I can add some operator overloading...

With the operators I can set my subview equal to e.g. the top, leading and trailing anchors of the parent view

view < subview
view > subview
view ^ subview

And because I'm returning the parent view in each operator I can set my view's top, leading and trailing anchors all on one line:

((view < subview) > subview) ^ subview

Yeah, that's more like it, entirely unreadable. Now I've definitely gone too far. that's a good place to stop.