Painless NSLayoutAnchors
Tags: swift, ios
Published: 2019-11-18 20:07:26


Creating layouts is a huge part of every iOS developer work. Working with UIKit is one of the basics which we need to master. From simple login screens where you have two text fields and a button to more complex screens with nested stack views, custom collection view cells where there is also a need to implement animations improving user experience.

Positioning views can be done in a few ways:

-   Manually setting views frame position
-   Storyboards
-   Layout constraints created in code

  
The first option is probably used only by masochists. Storyboards are ideal for prototyping and building small apps. Collaboration when using storyboards can be very hard and  resolving  merge conflicts can make you cry. So this leaves us with the third option. In Netguru we decided that our policy is to always build our views using code. It makes collaboration easier.

  
Some of us may remember times when a common way of adding layout constraints was using Visual Format Language. For those who do not remember what was that about, here is a “simple” snippet:

```swift
let verticalConstraints = NSLayoutConstraint.constraints(
    withVisualFormat: "V:|-40-[logoImageView(50)]-[loginTextField]-20-[passwordTextField]-20-[loginButton]-15@750-|",
    options: [],
    metrics: nil,
    views: views
)
```
Simple? Not at all. It’s not very descriptive. It takes a huge amount of time to master VFL. Because of that and a fact that  _NSLayoutConstraint_  API was not very friendly, a lot of libraries for adding constraints were created. You probably heard about PureLayout, SnapKit, Masonry, EasyPeasy and many many others. All of those were trying to make adding constraints easier. I used some of those and none of them made me spend a lot of time to learn how to create constraints using those.

  
Some time ago, Apple decided to improve our lives and introduced  _NSLayoutAnchor_. At first glance, it looks very nice. Every view have the same set of anchors that we can use for creating constraints. Let's take a look at the example:

```swift
let topConstraint = logoView.topAnchor.constraint(equalTo: self.topAnchor, constant: 50)
let heightConstraint = emailTextField.heightAnchor.constraint(equalToConstant: 100)
let widthConstraint = emailTextField.widthAnchor.constraint(equalToConstant: 100)
```
So, where is the pain?

Actually, that’s not all the code you need to write. To make it work you need to set _translatesAutoresizingMaskIntoConstraints_ flag  to false for every view that you will be setting up. Next thing is that every constraint is not active after creation. To make it all work we need code like this:
```swift
self.translatesAutoresizingMaskIntoConstraints = false
logoView.translatesAutoresizingMaskIntoConstraints = false
logoView.topAnchor.constraint(equalTo: self.topAnchor, constant: 50).isActive = true
logoView.topAnchor.constraint(equalTo: self.topAnchor, constant: 50).isActive = true
logoView.heightAnchor.constraint(equalToConstant: 100).isActive = true
logoView.widthAnchor.constraint(equalToConstant: 100).isActive = true
```

And remember, UIKit will not help you. If you will forget about setting  correct value  to  _translatesAutoresizingMaskIntoConstraints_  or  _isActive_  you can end up debugging layout issues from code that “should work”. Trust me, been there, done that.

  
How can we deal with layouts without using any external dependency and without all  _NSLayoutAnchor_  boilerplate.  First thing  that comes to mind is to write  micro framework  that will wrap creating constraints using  _NSLayoutAnchors_. Before writing any helper for that I came across a  [post](http://chris.eidhof.nl/post/micro-autolayout-dsl/)  written by Chris Eidhof describing how to approach this problem. His idea is to use key paths. How great is that? I loved it from  beginning. I will not describe how it works in detail, Chris did a great job explaining this in  [his post](http://chris.eidhof.nl/post/micro-autolayout-dsl/), there is even a free  [Swift Talk](https://talk.objc.io/episodes/S01E75-auto-layout-with-key-paths)  about it. You should check it out.

  
If Chris did all the necessary work, where is my part here?  Actually  his implementation is missing some of  features  that I needed. He handled case only when we want to add constraints along with adding  view  as  subview. It was not possible to add relations between two views using his solution. I’ve got cracking and came up with  small wrapper, that in my opinion helps  creating  constraints a lot. By small I mean, that it has around 200 lines of code of which most are docs.

Syntax  looks like this:
```swift
loginTextField.addConstraints([ 
    equal(\.heightAnchor, to: 30), 
    equal(logoImageView, \.topAnchor, \.bottomAnchor, constant: 20), 
    equal(superView, \.leadingAnchor, constant: 20), 
    equal(superView, \.trailingAnchor, constant: -20) 
])
```
  
What does this do is:

-   makes view 30pts height
-   attaches top border to bottom border of logoView with 20pts spacing
-   attaches leading and trailing anchors to corresponding anchors of superView with offset 20pts

  
I hope that without my explanation you could easily tell how this works. If yes, then  i  can consider my goal  achieved.

  
For more info please refer to the code and documentation for the wrapper. If you want to play with this, I’ve prepared swift playground with one simple view created.


[Wrapper](https://gist.github.com/Siemian/9781a0a2c03bc11b00c9c8b0325b36b6)
[SwiftPlayground](https://www.netguru.com/hubfs/PainlessNSLayoutAnchors.playground.zip)