Auto Layout in iOS: How to avoid the common mistake

I can’t remember how many times the following happened to me: I add a view to an existing view hierarchy in my code and it doesn’t show up when running the app. Even though I know why that happens, it’s still frustrating. If I add several new views at once I have to search for the one place where I forgot to set the translatesAutoresizingMaskIntoConstraints to false. Are you also sick of that? There is a solution.

First, let’s take a closer look at what’s happening behind the scenes.
When adding a new view using Auto Layout, it’s a common thing to initialize the new view with CGRectZero since you will provide a set of constraints for that view anyway. By default, the system translates the view’s autoresizing mask into a set of constraints fully specifying the view’s size and position. This also means by default you cannot add constraints to modify the view’s frame without introducing conflicts. The view will be displayed at position (0,0) with width and height of 0. To opt out of the default behaviour you have to remember to set translatesAutoresizingMaskIntoConstraints to false before activating the constraints.

This is what adding a view usually looks like when using Auto Layout:

let newView = UIView(frame: CGRectZero)

addSubview(newView)

newView.translatesAutoresizingMaskIntoConstraints = false // <- never forget this line

NSLayoutConstraint.activateConstraints([
    newView.topAnchor.constraintEqualToAnchor(topAnchor),
    newView.leftAnchor.constraintEqualToAnchor(leftAnchor),
    newView.widthAnchor.constraintEqualToConstant(100),
    newView.heightAnchor.constraintEqualToConstant(100)])

In case you are an iOS developer you will probably understand my pain. To all others: With these few lines of code it might not be obvious that it’s pretty easy to forget the one line that makes the difference. In reality, it’s often not a regular UIView but a UILabel, UITextField, UIButton or anything else subclassing UIView and thus, you will have a lot more lines setting up the background, the label’s text and other properties of the specific view. So you need to keep all this in mind and think about the correct layout constraints – which is often a quite tricky thing. In this situation, it can easily happen that you forget to turn off translatesAutoresizingMaskIntoConstraints.

However, writing a class extension for NSLayoutConstraints which adds a new method for activating constraints solves this problem forever. The method sets translatesAutoresizingMaskIntoConstraints to false for the view participating as first object in each of the constraints before activating the constraints.

extension NSLayoutConstraint {

    public class func useAndActivateConstraints(constraints: [NSLayoutConstraint]) {
        for constraint in constraints {
            if let view = constraint.firstItem as? UIView {
                 view.translatesAutoresizingMaskIntoConstraints = false
            }
        }
        activateConstraints(constraints)
    }
}

It’s much easier to remember to use this method as replacement for the original activateConstraints than to remember to turn off the flag every time. And even when I forget to use it and a view is missing in my app, I can still search for all occurrences of NSLayoutContraints.activateConstraints in my code and replace it.

TAGS

Comments

Please accept our cookie agreement to see full comments functionality. Read more

Find us on