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.
- It was still a little boilerplatey
- 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.