Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping memory address of unsatisfyable NSLayoutConstraints to UI controls

I appreciate that XCode will catch the unsatifyable NSLayoutConstraints and gives you some information about it, however I do not know how to take the memory addresses of the controls/constraints and map that to the actual controls that have problem.

My app is fairly large (around 20 screens), some are large UIViewControllers with several child view controller within them. Some are UITableViews with custom cells and UICollectionViews with custom cells. I'm having a hell of a time tacking down the cause of this error (which happens on landscape rotation)

Here is the information from my console:

2013-05-02 11:18:53.225 Smile[7519:c07] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x16083b60 h=-&- v=-&- UIView:0xa5a1d00.width == UIWindow:0xa09eee0.width>",
    "<NSLayoutConstraint:0xa5a2180 V:[UIView:0xa59f160]-(954)-|   (Names: '|':UIView:0xa5a1d00 )>",
    "<NSLayoutConstraint:0xa5a2140 V:|-(0)-[UIView:0xa59f160]   (Names: '|':UIView:0xa5a1d00 )>",
    "<NSAutoresizingMaskLayoutConstraint:0xa593340 h=--- v=--- H:[UIWindow:0xa09eee0(768)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0xa5a2180 V:[UIView:0xa59f160]-(954)-|   (Names: '|':UIView:0xa5a1d00 )>

Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

As you can see, there are memory addresses listed. Pasting those into the watch window's search bar doesn't reveal much. Also sifting though the thread and queue call stacks, I only get disassembled code on this breakpoint (exception breakpoint).

like image 515
VaporwareWolf Avatar asked May 02 '13 18:05

VaporwareWolf


2 Answers

I found kind of a solution thanks to these websites: Debugging iOS AutoLayout Issues and Dancing with the Debugger. Thanks to the Xcode console, you can identify from which view the problem comes from.

The solution:

  • Step 1:

    • Open the Breakpoint Navigator (cmd+7)
    • Click the 'Add' button in the lower left
    • Select 'Add Symbolic Breakpoint'
    • Where it says 'Symbol' type in UIViewAlertForUnsatisfiableConstraints
  • Step 2: Run your project. When the console prints Will attempt to recover by breaking constraint <NSLayoutConstraint:0x7fc82d3e18a0 H:[UIView:0x7fc82aba1210(768)]>, copy the memory adress of the view (0x7fc82aba1210 here).

  • Step 3: Change the background of this view to know which one it is with this command in the console: e (void)[0x7fc82d3e18a0 setBackgroundColor:[UIColor redColor]]

  • Step 4: Continue the program and see the updated UI. The view with red background color is the view in question.

  • Step 5: Go back to your IB and find which constraint is the problem on this view.

like image 59
Marie Dm Avatar answered Sep 22 '22 15:09

Marie Dm


The short answer is not really, OS X has an identifier property that can be set on a view that is used by NSLayoutConstraint to help identify views but that doesn't exist on iOS yet.

However there is a 'work around' to implement something similar on iOS but it isn't pretty. First you have to establish a name for each view, this accomplished in the UIView category. Then you have to update the output for NSLayoutConstraint description to use the new tags.

@interface NSLayoutConstraint (Nametags)
- (NSString *)asciiArtDescription;
@end

@interface UIView (Nametags)
@property (nonatomic, strong) NSString *nametag;
@end

Then provide the implementation as follows to change the output of NSLayout

#import <objc/objc-runtime.h>

static const char nametag_key;
@implementation NSLayoutConstraint (Nametags)

- (NSString *)description {
    NSMutableString *myDescription = [NSMutableString stringWithFormat:@"%@, ", [self asciiArtDescription]];
    UIView *firstView = (UIView *)[self firstItem];
    if (firstView) {
        [myDescription appendFormat:@"First View: 0x%0x: %@, ", (int)firstView, firstView.nametag];
    }

    UIView *secondView = (UIView *)[self secondItem];
    if (secondView) {
        [myDescription appendFormat:@"Second View: 0x%0x: %@", (int)secondView, secondView.nametag];
    }

    return myDescription;
}
@end

@implementation UIView (Nametags)
- (id) nametag {
    return objc_getAssociatedObject(self, (void *) &nametag_key);
}

- (void)setNametag:(NSString *) theNametag {
    objc_setAssociatedObject(self, (void *) &nametag_key, theNametag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end

Then you will be able to set names on your views like so

self.view.nametag = @"MyUsefulName";

Or define them in IB

nametag in IB

Finally when ever you dump constraints you will get the names you provided in the output.

"V:|-(202)-[UIView:0x75606f0], First View: 0x75606f0: Blue View, Second View: 0x7560bd0: (null)",
"V:[UIView:0x75606f0]-(72)-|, First View: 0x7560bd0: (null), Second View: 0x75606f0: Blue View",
"V:[UIView:0x7560bd0(548)], First View: 0x7560bd0: (null), "

You should only use this code in your debug builds, don't ship it in an app because it accesses the private [NSLayoutConstraint asciiArtDescription] method.

like image 20
Cory Powers Avatar answered Sep 24 '22 15:09

Cory Powers