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 and 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-5 (top). The parent view does not express ambiguous layout but its child views do:
HelloWorld[76351:c07] <UIView:0x715a9a0>: Unambiguous HelloWorld[76351:c07] <TestView:0x715add0>: Ambiguous HelloWorld[76351:c07] <TestView:0x715c9e0>: Ambiguous
You can run these tests as soon as you like—in loadView or wherever you set up new views and add constraints. It’s generally a good first step for 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.
You do not need to test any views produced in Interface Builder. IB guarantees that the rules it produces are unambiguous.
Exercising Ambiguity
Apple offers a curious tool in the form of its exerciseAmbiguityInLayout view method. It 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-8 shows this call in action. Here, you see an OS X window with three underconstrained subviews. These views appear in gray (bottom left), green (top right), and tan (bottom right). Their positions have not been set programmatically, so they end up wherever Auto Layout places them. In this example, after exercising ambiguity (see Figure 1-8, right), the tan view moves to the bottom left.
Figure 1-8. Exercising ambiguity allows you to change view frames to other legal values allowable 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, the green and gray views are unchanged, even though they also had ambiguous layout. Don’t relay exercising ambiguity to exhaustively find issues in your project, although it can be a useful tool for the right audience.
I am not a big fan of this feature, but, to be absolutely honest, it has helped me out of a pickle or two.
Visualizing Constraints
A purple outline surrounds the window in Figure 1-8. This 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 constraints you want to view.
Here is a simple way to exhaustively grab the constraints from a view and all its subviews 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 offers the Exercise Ambiguity button, which means that you don’t have to call the option from your own code.
This option also shows you the constraints you passed as clickable blue lines, helping you visualize those constraints in a live application. Click any item to log it to the Xcode debugging console.
All of these methods—testing for ambiguous layout, exercising that layout ambiguity, and visualizing constraints—are meant for development builds only. Don’t ship production code that calls them.