Photo by Bozhin Karaivanov on Unsplash
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:
UILabelthat contains the title of the dialog. The label is wrapped in a
UIViewin case the label has to be multiline
UILabelthat contains the main text content of the dialog. And again, to make the multiline stuff work, it is wrapped in a
[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. 🎉
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
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
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?
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
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