Debugging Autolayout issues in the Xcode

With the release of new iPhone devices in addition to older models iPhone4 and iPhone5, developers are forced to switch to autolayout instead of using manual frames to update our view layouts.

As we extensively use autolayout, goof-ups are very much likely to happen. I always miss something or add unnecessary constraints which sometimes crashes an app or spit out unwanted warnings in the console.

Today, I will tell you my story of how I debugged breaking constraints in the form of this blog post. I will tell you techniques you will need to use to debug warnings, as well as how to decode cryptic console error messages to fix the breaking constraints.

First, to demonstrate my point, we will add breaking constraints to the view.


let sampleView = UIView()
sampleView.translatesAutoresizingMaskIntoConstraints = false
sampleView.backgroundColor = UIColor.yellow
sampleView.accessibilityIdentifier = "Sample View"
self.view.addSubview(sampleView)
        
self.view.accessibilityIdentifier = "Super View"
let views = ["sampleView": sampleView]
self.view.addConstraints(
    NSLayoutConstraint.constraints(withVisualFormat: "H:|[sampleView(100)]|", 
    options: NSLayoutConstraint.FormatOptions(rawValue: 0), 
    metrics: nil, 
    views: views)
)

self.view.addConstraints(
    NSLayoutConstraint.constraints(withVisualFormat: "V:|[sampleView(100)]", 
    options: NSLayoutConstraint.FormatOptions(rawValue: 0), 
    metrics: nil, 
    views: views)
)
        

As you can see above, I have intentionally added an ambiguous constraint

"H:|[sampleView(100)]|"

In order to better debug, I have also added accessibilityIdentifier to view and corresponding superview. This will help us to easily spot views with breaking constraints. When I run this app, I get the 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 an ambiguous layout.


Will attempt to recover by breaking constraint

<NSLayoutConstraint:0x600002afc910 H:[Sample View]-(0)-|   
(active, names: Super View:0x7fba4e5060b0, 
Sample View:0x7fba54204bf0, '|':Super View:0x7fba4e5060b0 )>
Note that since we have added accessbilityIdentifier to views, they are immediately recognizable in the debugger. This is a good trick to apply for getting easier readability

As suggested by the debugger, we will add a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints. This will halt the execution when ambiguous constraints are encountered.

  1. Go to Breakpoint navigator
  2. Click on + icon at the bottom-left corner
  3. Choose an option Symbolic Breakpoint
  4. Enter UIViewAlertForUnsatisfiableConstraints under symbol option
  5. Press enter
  6. Restart the app

As expected, it will halt the program execution and present with the following assembly code,

Initially, when I looked at this code, I didn't quite get what was going on here. But let's start debugging this assembly code by printing the information we need in order to debug autolayout issue.

You can go to debug console and type po $rbx. It will print description of views with breaking constraints as follows,

(lldb) po $rbx
<UIView: 0x7fe797f1aba0; 
frame = (0 0; 320 568); autoresize = W+H; 
layer = <CALayer: 0x60000109c2a0>>

This message can be used to inspect the size dimension of faulting view as well as its memory address for follow-up debugging steps

Now going back, let's take a look at debug log, we get the following addresses for respective views

  • Sample View: 0x7fba00e07c20 - View with breaking constraints
  • Super View: 0x7fba00e081d0 - A parent view associated with a view having breaking constraints

There are two ways to inspect this view.

1.  Through view debugger

This is the easiest way to capture the view with breaking constraints. When Xcode detects the view with breaking constraints, it will print a message which looks something like this,

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x600000f7d590 H:[Sample View]-(0)-|   
(active, names: Super View:0x7fba00e081d0, 
Sample View:0x7fba00e07c20, '|':Super View:0x7fba00e081d0 )>

Looking at this log, you will know that the view Sample View is undergoing breaking constraints and the address associated with it is 0x7fba00e07c20.

The next step we will do is to capture the view hierarchy and try to locate this view in this hierarchy,

  1. Go to Debug menu in Xcode
  2. Select View Debugging option
  3. Select Capture View Hierarchy

It will display details view hierarchy as follows,

While Xcode is displaying this view hierarchy in the left menu bar, filter this hierarchy by entering the view address in the bottom filter menu and it will immediately highlight the view with breaking constraints.

Once you locate this view, you can take the next steps to analyze why constraints are breaking and what you can do to fix them.

2. Through Debug console

If you want to directly highlight views with breaking constraints for visual inspection, you can even alter their properties on runtime using memory addresses as follows,

In Xcode,

  1. Go to Debug menu
  2. Select Pause

The program will pause and then you can go to debug console and execute the following command,


(lldb) expr ((UIView *)0x7fba00e07c20).backgroundColor = [UIColor purpleColor]

Where,

  • expr is used to evaluate runtime expression in the debugger
  • 0x7fba00e07c20 is the address of view we are trying to debug - in our case view with breaking constraints
  • (UIView *) is used to cast memory address into UIView object
  • Once this address is cast, we can treat it as UIView object and update the property

Please note that sometimes after evaluating UIView subclass appearance from a command line like this won't immediately update the UI. iOS usually waits until the next run loop or any user event such as tap or swipe occurs on the app. You can force the render server to update the display UI with the 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 an address of a suspicious view and change its appearance 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 constraints and see if you have added them correctly
  • Watch out for incomplete as well as ambiguous constraint rules
  • Use accessbilityIdentifier to uniquely identify views with breaking constraints in the debugger - this will help you easily locate view with breaking constraint just looking at the debug log
  • Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to break and catch this error in the debugger
  • This is a generic mistake for iOS newbies - when you create a view and add a constraint to it programmatically, make sure to turn the autoresizing mask off by using [your_view].translatesAutoresizingMaskIntoConstraints = false. (When you do it in the storyboard or xibs, Interface builder will automatically turn it off 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:

Debugging iOS AutoLayout Issues - Developing on Staxmanade
This tip may not be news to you, but it was to me so I’ll put this up here to help you, but more to help myself when I get stuck down the road and for...