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).
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:
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.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With