With the release of new iPhone devices in addition to older models iPhone4 and iPhone5, we developers are all forced to switch to autolayout instead of using manual frames to update view layout.
As we extensively use autolayout, goof ups are very much likely. I always miss something or add unnecessary constraint which sometimes crash an app or spit out nasty warnings in the console.
Today, I will tell you my story of how I debugged constraints in the form of this blog post. I will tell you techniques you will need to use to debug potential warnings, as well as how to decode assembly to deal with breaking constraints.
First to demonstrate my point, we will add breaking constraints to the view.
let sampleView = UIView() sampleView.translatesAutoresizingMaskIntoConstraints = false sampleView.backgroundColor = UIColor.redColor() sampleView.accessibilityIdentifier = "Sample View" self.view.addSubview(sampleView) self.view.accessibilityIdentifier = "Super View" let views = ["sampleView": sampleView] self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|[sampleView(100)]|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views)) self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|[sampleView(100)]", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
As you can see above, I have intentionally added a ambiguous constraint
In order to better debug, I have also added
accessibilityIdentifier for view and corresponding superview. This will help us spot views with breaking constraint easier as follows. When I run this app, I get following debug log letting me know about broken constraints.
As it is clear from debug log, Xcode will also tell which constraint it is going to break as a result of ambiguous layout.
Will attempt to recover by breaking constraint <NSLayoutConstraint:0x7ffac9a22f80 H:[Sample View]-(0)-| (Names: Super View:0x7ffac9a1fde0, Sample View:0x7ffac9a15430, '|':Super View:0x7ffac9a1fde0 )>
Note that since we have added
accessbilityIdentifierto views, they are immediately recognizable in the debugger. This is a good trick to apply!
As suggested by debugger, we will add symbolic breakpoint at
UIViewAlertForUnsatisfiableConstraints. This will halt the execution when ambiguous constraints are encountered.
As expected, it will halt the program execution and present with following assembly code,
Just like you, I don't get what's going on there. So what I will do, go to debugger and type command
po $r15. This will give you the details description of constraint that is broken along with views and their addresses associated with it.
Update: Important Note
If for some reason,
po $r15option does not work, (As it did for me when I recently tried to debug autolayout issue) try it with
po $rbxit will give you an array containing all the conflicting constraints along with the views associated with it. Sample output from my recent run is as follows. As it shows in image below, there are 3 constraints - left edge, fixed width of 100 pixels and last one on the right edge. As it is clear from their content they're in conflicts with each other and by finding the views associated with them and manually coloring them (As described in the next paragraph) it is easy to debug and locate the list of views and constraints associated with them
If you want more detailed description of all constraints added in addition to breaking constraint, you can type
po $r14 in the debugger
Now looking at all this information, you know the list of constraints for view under consideration, breaking constraints and constraints taking precedence. You also have addresses of underlying views should they help in any way.
For e.g. Looking at debug log, we get following addresses for respective views
- Sample View -
- Super View -
If you want to highlight them for visual inspection, you can even alter their properties on runtime using memory addresses as follows,
(lldb) expr ((UIView *)0x7fac08e217c0).backgroundColor = [UIColor yellowColor]
expris used to evaluate runtime expression in debugger
0x7fac08e217c0is the address of view we are trying to debug
(UIView *)is used to cast memory address into
- Once this address is cast, we can treat it as
UIViewobject and update the property
Please note that sometimes after evaluating
UIView subclass appearance from command line like this won't immediately update the UI. iOS usually waits until next run loop or any user event such as tap or swipe occurs on the app. You can force render server to update the display UI with following command.
e (void)[CATransaction flush]
Using this trick, even if you are not able to locate faulted view in the debugger, you can grab a suspicious address and change the appearance of corresponding view to provide a visual cue on screen
As you can see I have changed the background color of view for which constraints were breaking and now it appears like this.
To summarize, in order to avoid breaking constraints scenario you can follow some of the tips below :
- Look at individual constraint and see if you have added them correctly
- Watch out for incomplete as well as ambiguous constraint rules
accessbilityIdentifierto uniquely identify views with breaking constraints in the debugger
- Make a symbolic breakpoint at
UIViewAlertForUnsatisfiableConstraintsto catch this error in debugger
- This is generic mistake for iOS newbies - when you create a view and add constraint to it programmatically, make sure to turn the autoresizing mask off by using
[view].translatesAutoresizingMaskIntoConstraints = false. (When you do it in the storyboard or xibs, IB will turn this off automatically for you.)
These are some of the steps I find useful while debugging autolayout issues. If you know any problems with them or have your own tips dealing with fixing autolayout issues, I would love to hear them
Reference : This post was inspired by the following blog post, originally published here