The setTranslatesAutoresizingMaskIntoConstraints Auto Layout Trap

tl;dr: When programmatically creating UIKit components with any Auto Layout constraints in mind, you need to call setTranslatesAutoresizingMaskIntoConstraints(false), or you’re gonna have a bad time (pretty much the worst time).

Details:

If you’re an iOS and iOS layout noob like myself—I only started making apps from scratch after Swift and iOS 8—you may have never gained a good understanding of the old autoresizing mask model of defining where things go in your app.

More importantly, unless you were supporting code making the transition from the old framework to the new one, you may not realize that when you instantiate components, the library will still add whatever old autoresizing constraints used to get created for positioning elements and install them as Auto Layout constraints.

That will likely bite you in the buttocks as soon as you decide to add any constraints of your own.

For example, say you were trying to add a little button to the right of a label on a table cell.

You envision this:

And so you add to your cell creation code something like this:

var button = UIButton(
frame: GCRect(x: 0, y: 0, width: 32, height: 32)
)
searchCell.contentView.addSubview(button)
// Omitting some button setup.
searchCell.contentView.addConstraints([
NSLayoutConstraint(
item: button,
attribute: .Baseline,
relatedBy: .Equal,
toItem: searchCell.label,
attribute: .Baseline,
multiplier: 1,
constant: 0
),
NSLayoutConstraint(
item: button,
attribute: .Right,
relatedBy: .Equal,
toItem: searchCell.contentView,
attribute: .Right,
multiplier: 1,
constant: -8
)
])

But instead get this result:

The button is stuck at the original position you gave it via the frame. If you try to initialize it with no frame, you’ll see the same result—or nothing at all if you never set its dimensions.

At this point, you’re likely to invest way too much of your time googling things and re-reading the excellent Auto Layout guide until you find this bit on the internets or in UIKit.h above a function called translatesAutoresizingMaskIntoConstraints:

by default, the autoresizing mask on a view gives rise to constraints that fully determine the view’s position. Any constraints you set on the view are likely to conflict with autoresizing constraints, so you must turn off this property first. IB will turn it off for you.

The last sentence meaning that whenever you add a constraint in Interface Builder, like you do a lot until you have a reason to instantiate things in code, setTranslatesAutoresizingMaskIntoConstraints(false) will get automatically called for you behind the scenes.

Armed with this new knowledge, you can now try:

var button = UIButton()
searchCell.contentView.addSubview(button)
// Omitting some button setup.
button.setTranslatesAutoresizingMaskIntoConstraints(false)
button.addConstraints([
NSLayoutConstraint(
item: button,
attribute: .Width,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1,
constant: CGFloat(32)
),
NSLayoutConstraint(
item: button,
attribute: .Height,
relatedBy: .Equal,
toItem: nil,
attribute: .NotAnAttribute,
multiplier: 1,
constant: CGFloat(32)
)
])
searchCell.contentView.addConstraints([
NSLayoutConstraint(
item: button,
attribute: .Baseline,
relatedBy: .Equal,
toItem: searchCell.label,
attribute: .Baseline,
multiplier: 1,
constant: 0
),
NSLayoutConstraint(
item: button,
attribute: .Right,
relatedBy: .Equal,
toItem: searchCell.contentView,
attribute: .Right,
multiplier: 1,
constant: -8
)
])

Which gets you the desired result.

As a bonus, because I’m that nice, a couple of useful Auto Layout debugging tips:

You can inspect this useful internal trace to look for stuff like extraneous constraints, ambiguous positions, and so on:

po [[UIWindow keyWindow] _autolayoutTrace]

Also, try going through these steps if you’re looking for debugging inspiration.

Feel free to hit me up with corrections. Thanks, Jeff Verkoeyen, for proof-reading.

References:

  1. http://www.informit.com/articles/article.aspx?p=2041295
  2. Apple’s Auto Layout Guide: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/Introduction/Introduction.html