How I debug broken layout constraints in AutoLayout

ยท

4 min read

While navigating our app at work, I try to keep an eye on the debug output and find warnings about broken layout constraints. This happens from time to time because we code all of our views and use AutoLayout to create the layouts. So nothing keeps us from coding conflicting constraints. ๐Ÿ˜„

A few days ago I was working on a small feature that used a simple dialog of ours. The dialog is pretty simple. It uses a UIStackView at its core. In there, we have the following components:

  • a UILabel that contains the title of the dialog. The label is wrapped in a UIView in case the label has to be multiline

  • another UILabel that contains the main text content of the dialog. And again, to make the multiline stuff work, it is wrapped in a UIView

  • [there could be more stuff in here ...]

  • a close button at the bottom

When I started the app and opened the dialog, I saw the following lines in the debug output:

(
    "<NSLayoutConstraint:0x600001214e10 H:|-(25)-[UIView:0x12760c3b0]   (active, names: '|':UIStackView:0x140807fd0 )>",
    "<NSLayoutConstraint:0x6000012326c0 'UISV-alignment' UIView:0x12760c080.leading == UIView:0x12760c3b0.leading   (active)>",
    "<NSLayoutConstraint:0x6000012328f0 'UISV-canvas-connection' UIStackView:0x140807fd0.leading == UIView:0x12760c080.leading   (active)>"
)

To be honest, I hate those outputs. I mean, if I take enough time I'm able to get what it says, but it's just not an easy thing to do. You know what? You don't have to spend a lot of time trying to understand these lines. There is an app a tool for that. It's called wtfautolayout.com. It's just brilliant. You copy the lines from above and paste them into the textbox on the website. Now, press the "Go!" button. ๐ŸŽ‰

A visual representation of the broken layout constraints created by wtfautolayout.com

Now, THAT'S human-readable output. ๐Ÿ‘

Making the output even better

Depending on the complexity of your view, this output could still use some more context. It tells us something about a View1, a StackView and a View2. Wow... ๐Ÿ™„ Our current screen consists of multiple views. So which one is it, that breaks these constraints?

Accessibility Identifiers to the rescue

You can use the accessibilityIdentifier property of any UIView to improve the output. Let's look at our example:

let titleWrapper = UIView()
titleWrapper.accessibilityIdentifier = "titleWrapper"
...

Just a little warning: I use these accessibility identifiers only temporarily here because "titleWrapper" won't give you any real value as an accessibility identifier. I use it just for debugging purposes in this case.

If you provide these identifiers to any view on the screen that's giving you the constraint warning, your output will change to something like this:

(
    "<NSLayoutConstraint:0x6000013c3610 H:|-(25)-[contentWrapper]   (active, names: contentWrapper:0x12ed10ff0, '|':UIStackView:0x12ed0b6b0 )>",
    "<NSLayoutConstraint:0x6000013a3a20 'UISV-alignment' titleWrapper.leading == contentWrapper.leading   (active, names: titleWrapper:0x12ed0db90, contentWrapper:0x12ed10ff0 )>",
    "<NSLayoutConstraint:0x6000013a38e0 'UISV-canvas-connection' UIStackView:0x12ed0b6b0.leading == titleWrapper.leading   (active, names: titleWrapper:0x12ed0db90 )>"
)

Let's throw that at wtfautolayout.com again:

So now we know which specific views are causing the problem. But while looking at the constraints, I noticed that something is wrong. I never specified constraints between titleWrapper and contentWrapper. ๐Ÿค” The first constraint (C == S + 25) is there. I can see it in the code (we create our views with AutoLayout programmatically). It was created to ident the text content. But I cannot find the other constraints. Where do they come from?

What happened?

If you have looked carefully at the screenshot, you have probably already seen the solution. At the bottom of the screenshot, it says

โ€ก This constraint was added by a stack view to enforce its spacing, distribution or alignment

The UIStackView created these two "mysterious" constraints internally for good reasons. In case you experience the same problem in the future, you will remember what happened. And even if you don't, wtfautolayout.com will tell you. ๐Ÿ˜‰

Side note: What was the fix for me?

Pretty simple. I already wrapped the UILabel for the content in an additional UIView. So I can just move the "indentation constraint" from the wrapper view to the label. That way, the wrapper view can still comply with the constraints created by the UIStackView.

ย