How I debug broken layout constraints in AutoLayout
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 aUIView
in case the label has to be multilineanother
UILabel
that contains the main text content of the dialog. And again, to make the multiline stuff work, it is wrapped in aUIView
[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 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
.
Subscribe to my newsletter
Read articles from Lars Richter directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Lars Richter
Lars Richter
Head of Engineering @ Parship Group by day, developer (.NET and iOS) by night. From Hamburg, Germany.