Ambiguous Layout
During development, you can test whether a view’s constraints are sufficient by calling hasAmbiguousLayout. This returns a Boolean value of YES for a view that could have occupied a different frame or NO for a view whose constraints are fully specified.
These results are view specific. For example, imagine a fully constrained view whose child is underconstrained. The view itself does not have ambiguous layout, even though its child does. You can and should test the layout individually for each view in your hierarchy, as follows:
@implementation VIEW_CLASS (AmbiguityTests) // Debug only. Do not ship with this code - (void) testAmbiguity { NSLog(@"<%@:0x%0x>: %@", self.class.description, (int)self, self.hasAmbiguousLayout ? @"Ambiguous" : @"Unambiguous"); for (VIEW_CLASS *view in self.subviews) [view testAmbiguity]; } @end
This code descends through a view hierarchy and lists the results for each level. Here’s what a simple layout with two subviews returned for the underconstrained layout code originally shown in Figure 1-3 (top):
HelloWorld[76351:c07] <UIView:0x715a9a0>: Unambiguous HelloWorld[76351:c07] <TestView:0x715add0>: Ambiguous HelloWorld[76351:c07] <TestView:0x715c9e0>: Ambiguous
The superview does not express ambiguous layout, but its child views do.
You can run tests for ambiguous layout as soon as you like—in loadView or wherever you set up new views and add constraints. It’s generally a good first step to take any time you’re adding new views to your system as well. It ensures that your constraints really are as fully specified as you think they are.
Use these tests during development but do not ship them in App Store code. They help you check your layouts as you incrementally build interfaces.
Exercising Ambiguity
Apple offers a curious tool in the form of its exerciseAmbiguityInLayout view method. This method automatically tweaks view frames that express ambiguous layouts. This is a view method (UIView and NSView) that checks for ambiguous layout and attempts to randomly change a view’s frame.
Figure 1-6 shows this call in action. Here, you see an OS X window with three underconstrained subviews. Their positions have not been set programmatically, so they end up wherever Auto Layout places them. In this example, after you exercise ambiguity (see Figure 1-6, right), the light-colored view, initially at the bottom right, moves to the bottom left.
Figure 1-6 Exercising ambiguity allows you to change view frames to other legal values that are allowed under your current set of Auto Layout constraints.
This tells you that (1) this is one of the affected underconstrained views and (2) you can see some of the range that might apply to this view due to its lack of positioning constraints.
Exercising ambiguity is a blunt and limited weapon. In this example, some views are unchanged, even though they also had ambiguous layout. You shouldn’t rely on exercising ambiguity to exhaustively find issues in your project, although it can be a useful tool for the right audience. Exercising ambiguity won’t cure cancer or create world peace, but it has helped me out of a (rare) pickle or two.
Visualizing Constraints
The purple outline that surrounds the window in Figure 1-6 is an OS X–only feature. On OS X, you can visualize constraints by calling visualizeConstraints: on any NSWindow instance. You pass it an array of constraint instances that you want to view.
Here is a simple way to exhaustively grab the constraints from a view and all its subviews, by using simple class extension:
@implementation VIEW_CLASS (GeneralConstraintSupport) // Return all constraints from self and subviews - (NSArray *) allConstraints { NSMutableArray *array = [NSMutableArray array]; [array addObjectsFromArray:self.constraints]; for (VIEW_CLASS *view in self.subviews) [array addObjectsFromArray:[view allConstraints]]; return array; } @end
The purple backdrop that appears tells you whether the window’s layout is ambiguous. It tests from the window down its view hierarchy, all the way to its leaves. If it finds any ambiguity, it makes the Exercise Ambiguity button available, which means you don’t have to call the option from your own code.
This visualization option also shows you the constraints you passed as clickable blue lines, helping you locate those constraints in a live application. You can click any item to log it to the Xcode debugging console.