Debugging Constraints

You have added constraints to BNRDetailViewController.xib that will allow Auto Layout to determine alignment rectangles for every view in the hierarchy and give you the layout that you want on the iPhone and the iPad. Given the sheer number of constraints, it is easy to introduce problems. You can miss a constraint, you could have constraints that conflict, or you could have a constraint that conflicts with how a view appears on the canvas.

Fortunately, there are several tools you can use to debug Auto Layout constraints. Let’s take a look at each of these in turn and see ways to fix them.

Ambiguous layout

An ambiguous layout occurs when there is more then one way to fulfill a set of constraints. Typically, this means that you are missing at least one constraint.

Currently, there are no ambiguous layouts, so let’s introduce some. Add two labels to the UIControl under the dateLabel and position them next to one another. In the attributes inspector, change the background color of the labels to light gray so that you can see their frames. The interface should look like Figure 15.16.

Figure 15.16  New labels

New labels

Now let’s add some constraints to both labels. Hold down the Shift key and select both labels. Open the Pin Auto Layout menu, select the top, left, and right struts at the top, and then click Add 5 Constraints (Figure 15.17).

Figure 15.17  Adding constraints to multiple views at once

Adding constraints to multiple views at once

You have pinned three edges for two labels, so you may be wondering Why 5 constraints and not 6? The answer is that the trailing edge constraint of the label on the left and the leading edge constraint of the label on the right are the same constraint. Interface Builder recognizes this and only adds one constraint to satisfy both attributes.

If you build and run on an iPhone, everything will look fine, but running on the iPad is a different story. Build and run the project on an iPad and navigate to the detail interface. One of the two labels is wider than the other (Figure 15.18).

Figure 15.18  Width of labels is surprising on iPad

Width of labels is surprising on iPad

These labels do not have enough constraints to unambiguously define their frames. Auto Layout takes its best guess at runtime, and it is not what you wanted on the iPad. You are going to use two UIView methods, hasAmbiguousLayout and exerciseAmbiguousLayout, to debug this situation.

Open BNRDetailViewController.m. Override the method viewDidLayoutSubviews to check if any of its subviews has an ambiguous layout.


-​ ​(​v​o​i​d​)​v​i​e​w​D​i​d​L​a​y​o​u​t​S​u​b​v​i​e​w​s​
{​
 ​ ​ ​ ​f​o​r​ ​(​U​I​V​i​e​w​ ​*​s​u​b​v​i​e​w​ ​i​n​ ​s​e​l​f​.​v​i​e​w​.​s​u​b​v​i​e​w​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​(​[​s​u​b​v​i​e​w​ ​h​a​s​A​m​b​i​g​u​o​u​s​L​a​y​o​u​t​]​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​(​@​"​A​M​B​I​G​U​O​U​S​:​ ​%​@​"​,​ ​s​u​b​v​i​e​w​)​;​
 ​ ​ ​ ​}​
}​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​

viewDidLayoutSubviews gets called any time the view changes in size (for example, when the device is rotated) or when it is first presented on the screen.

Build and run the application on the iPad simulator and navigate to the BNRDetailViewController. Then check the console; it will report that the two labels are ambiguous. (Note that the output in the console is often duplicated, resulting in twice as many messages as you would expect.)

You can go a step further to actually see the other way this layout might appear. In BNRDetailViewController.m, edit the backgroundTapped: method to send the message exerciseAmbiguityInLayout to any ambiguous views.

-​ ​(​I​B​A​c​t​i​o​n​)​b​a​c​k​g​r​o​u​n​d​T​a​p​p​e​d​:​(​i​d​)​s​e​n​d​e​r​
{​
 ​ ​ ​ ​[​s​e​l​f​.​v​i​e​w​ ​e​n​d​E​d​i​t​i​n​g​:​Y​E​S​]​;​
 ​ ​ ​ ​
 ​ ​ ​ ​f​o​r​ ​(​U​I​V​i​e​w​ ​*​s​u​b​v​i​e​w​ ​i​n​ ​s​e​l​f​.​v​i​e​w​.​s​u​b​v​i​e​w​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​[​s​u​b​v​i​e​w​ ​h​a​s​A​m​b​i​g​u​o​u​s​L​a​y​o​u​t​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​s​u​b​v​i​e​w​ ​e​x​e​r​c​i​s​e​A​m​b​i​g​u​i​t​y​I​n​L​a​y​o​u​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​
}​

Build and run the application again. Once on the BNRDetailViewController, tap the background view anywhere. The width of the two labels will swap.

Figure 15.19  Tapping in the background demonstrates the other possible layout

Tapping in the background demonstrates the other possible layout

Neither of the widths of the labels has been constrained, and so there is more than one solution to the system of Auto Layout equations. Because of this, there is an ambiguous layout and tapping the background switches between the two possible solutions. Due to the other constraints you have specified, as long as one of the labels has its width constrained, the other label’s width can be determined. You will get rid of the ambiguous layout by giving the two labels equal widths.

In BNRDetailViewController.xib, Control-drag from one label to the other, and then select Equal Widths. Build and run the application on iPad. Your labels have the same width. Check the console to confirm that there are no more ambiguous layouts. Tapping on the background will have no effect on the interface.

Your interface is once again properly set up. All views have enough constraints to fully construct their alignment rectangle, and so there are no more ambiguous views.

In BNRDetailViewController.xib, select and delete the two test labels.

The exerciseAmbiguityInLayout method is purely a debugging tool that allows Auto Layout to show you where your layouts could potentially end up. You should never leave this code in an application that you are shipping.

In BNRDetailViewController.m, delete viewDidLayoutSubviews and delete the code that calls exerciseAmbiguityInLayout in backgroundTapped:.

-​ ​(​v​o​i​d​)​v​i​e​w​D​i​d​L​a​y​o​u​t​S​u​b​v​i​e​w​s​
{​
 ​ ​ ​ ​f​o​r​ ​(​U​I​V​i​e​w​ ​*​s​u​b​v​i​e​w​ ​i​n​ ​s​e​l​f​.​v​i​e​w​.​s​u​b​v​i​e​w​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​(​[​s​u​b​v​i​e​w​ ​h​a​s​A​m​b​i​g​u​o​u​s​L​a​y​o​u​t​]​)​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​N​S​L​o​g​(​@​"​A​M​B​I​G​U​O​U​S​:​ ​%​@​"​,​ ​s​u​b​v​i​e​w​)​;​
 ​ ​ ​ ​}​
}​

-​ ​(​I​B​A​c​t​i​o​n​)​b​a​c​k​g​r​o​u​n​d​T​a​p​p​e​d​:​(​i​d​)​s​e​n​d​e​r​
{​
 ​ ​ ​ ​[​s​e​l​f​.​v​i​e​w​ ​e​n​d​E​d​i​t​i​n​g​:​Y​E​S​]​;​

 ​ ​ ​ ​f​o​r​ ​(​U​I​V​i​e​w​ ​*​s​u​b​v​i​e​w​ ​i​n​ ​s​e​l​f​.​v​i​e​w​.​s​u​b​v​i​e​w​s​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​i​f​ ​(​[​s​u​b​v​i​e​w​ ​h​a​s​A​m​b​i​g​u​o​u​s​L​a​y​o​u​t​]​)​ ​{​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​ ​[​s​u​b​v​i​e​w​ ​e​x​e​r​c​i​s​e​A​m​b​i​g​u​i​t​y​I​n​L​a​y​o​u​t​]​;​
 ​ ​ ​ ​ ​ ​ ​ ​}​
 ​ ​ ​ ​}​
}​

Unsatisfiable constraints

The problem of unsatisfiable constraints occurs when two or more constraints conflict. This often means that a view has too many constraints. To illustrate, let’s introduce this problem to the BNRDetailViewController.

In BNRDetailViewController.xib, select the label that displays the date. In the attributes inspector, change its background to light gray so that you can see its frame in the layout. Next, pin the width of this label to its current value.

Just as before, if you were to build and run the application on an iPhone, everything would be fine. Build and run the application on an iPad. The label may look just as it did before, but take a look at the console.

U​n​a​b​l​e​ ​t​o​ ​s​i​m​u​l​t​a​n​e​o​u​s​l​y​ ​s​a​t​i​s​f​y​ ​c​o​n​s​t​r​a​i​n​t​s​.​
P​r​o​b​a​b​l​y​ ​a​t​ ​l​e​a​s​t​ ​o​n​e​ ​o​f​ ​t​h​e​ ​c​o​n​s​t​r​a​i​n​t​s​ ​i​n​ ​t​h​e​ ​f​o​l​l​o​w​i​n​g​ ​l​i​s​t​ ​i​s​ ​o​n​e​ ​y​o​u​ ​d​o​n​'​t​ ​w​a​n​t​.​
T​r​y​ ​t​h​i​s​:​ ​(​1​)​ ​l​o​o​k​ ​a​t​ ​e​a​c​h​ ​c​o​n​s​t​r​a​i​n​t​ ​a​n​d​ ​t​r​y​ ​t​o​ ​f​i​g​u​r​e​ ​o​u​t​ ​w​h​i​c​h​ ​y​o​u​ ​d​o​n​'​t​ ​e​x​p​e​c​t​;​
(​2​)​ ​f​i​n​d​ ​t​h​e​ ​c​o​d​e​ ​t​h​a​t​ ​a​d​d​e​d​ ​t​h​e​ ​u​n​w​a​n​t​e​d​ ​c​o​n​s​t​r​a​i​n​t​ ​o​r​ ​c​o​n​s​t​r​a​i​n​t​s​ ​a​n​d​ ​f​i​x​ ​i​t​.​
(​N​o​t​e​:​ ​I​f​ ​y​o​u​'​r​e​ ​s​e​e​i​n​g​ ​N​S​A​u​t​o​r​e​s​i​z​i​n​g​M​a​s​k​L​a​y​o​u​t​C​o​n​s​t​r​a​i​n​t​s​ ​t​h​a​t​ ​y​o​u​ ​d​o​n​'​t​ ​u​n​d​e​r​s​t​a​n​d​,​
r​e​f​e​r​ ​t​o​ ​t​h​e​ ​d​o​c​u​m​e​n​t​a​t​i​o​n​ ​f​o​r​ ​t​h​e​ ​U​I​V​i​e​w​ ​p​r​o​p​e​r​t​y​
t​r​a​n​s​l​a​t​e​s​A​u​t​o​r​e​s​i​z​i​n​g​M​a​s​k​I​n​t​o​C​o​n​s​t​r​a​i​n​t​s​)​
(​
 ​ ​ ​ ​"​<​N​S​L​a​y​o​u​t​C​o​n​s​t​r​a​i​n​t​:​0​x​a​3​3​3​d​a​0​ ​H​:​[​U​I​L​a​b​e​l​:​0​x​a​3​3​3​c​a​0​(​2​8​0​)​]​>​"​,​
 ​ ​ ​ ​"​<​N​S​L​a​y​o​u​t​C​o​n​s​t​r​a​i​n​t​:​0​x​a​3​9​4​5​0​0​ ​H​:​[​U​I​L​a​b​e​l​:​0​x​a​3​3​3​c​a​0​]​-​(​2​0​)​-​|​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​N​a​m​e​s​:​ ​'​|​'​:​U​I​C​o​n​t​r​o​l​:​0​x​a​3​8​c​d​8​0​ ​)​>​"​,​
 ​ ​ ​ ​"​<​N​S​L​a​y​o​u​t​C​o​n​s​t​r​a​i​n​t​:​0​x​a​3​9​4​5​3​0​ ​H​:​|​-​(​2​0​)​-​[​U​I​L​a​b​e​l​:​0​x​a​3​3​3​c​a​0​]​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​(​N​a​m​e​s​:​ ​'​|​'​:​U​I​C​o​n​t​r​o​l​:​0​x​a​3​8​c​d​8​0​ ​)​>​"​,​
 ​ ​ ​ ​"​<​N​S​A​u​t​o​r​e​s​i​z​i​n​g​M​a​s​k​L​a​y​o​u​t​C​o​n​s​t​r​a​i​n​t​:​0​x​a​3​a​1​a​7​0​ ​h​=​-​&​-​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​v​=​-​&​-​ ​U​I​C​o​n​t​r​o​l​:​0​x​a​3​8​c​d​8​0​.​w​i​d​t​h​ ​=​=​ ​_​U​I​P​a​r​a​l​l​a​x​D​i​m​m​i​n​g​V​i​e​w​:​0​x​a​3​7​b​1​4​0​.​w​i​d​t​h​>​"​,​
 ​ ​ ​ ​"​<​N​S​A​u​t​o​r​e​s​i​z​i​n​g​M​a​s​k​L​a​y​o​u​t​C​o​n​s​t​r​a​i​n​t​:​0​x​a​3​a​2​1​d​0​ ​h​=​-​-​&​
 ​ ​ ​ ​ ​ ​ ​ ​ ​ ​v​=​-​-​&​ ​H​:​[​_​U​I​P​a​r​a​l​l​a​x​D​i​m​m​i​n​g​V​i​e​w​:​0​x​a​3​7​b​1​4​0​(​7​6​8​)​]​>​"​
)​

W​i​l​l​ ​a​t​t​e​m​p​t​ ​t​o​ ​r​e​c​o​v​e​r​ ​b​y​ ​b​r​e​a​k​i​n​g​ ​c​o​n​s​t​r​a​i​n​t​
<​N​S​L​a​y​o​u​t​C​o​n​s​t​r​a​i​n​t​:​0​x​a​3​3​3​d​a​0​ ​H​:​[​U​I​L​a​b​e​l​:​0​x​a​3​3​3​c​a​0​(​2​8​0​)​]​>​

B​r​e​a​k​ ​o​n​ ​o​b​j​c​_​e​x​c​e​p​t​i​o​n​_​t​h​r​o​w​ ​t​o​ ​c​a​t​c​h​ ​t​h​i​s​ ​i​n​ ​t​h​e​ ​d​e​b​u​g​g​e​r​.​
T​h​e​ ​m​e​t​h​o​d​s​ ​i​n​ ​t​h​e​ ​U​I​C​o​n​s​t​r​a​i​n​t​B​a​s​e​d​L​a​y​o​u​t​D​e​b​u​g​g​i​n​g​ ​c​a​t​e​g​o​r​y​ ​o​n​ ​U​I​V​i​e​w​ ​l​i​s​t​e​d​
i​n​ ​<​U​I​K​i​t​/​U​I​V​i​e​w​.​h​>​ ​m​a​y​ ​a​l​s​o​ ​b​e​ ​h​e​l​p​f​u​l​.​

First, Xcode informs you that it is unable to simultaneously satisfy constraints and gives you some hints on what to look for. The console then lists all of the constraints that are related to the issue. Finally, you are told that one of the constraints will be ignored so that the label will have a valid frame. In this case, the constraint to pin the width will be ignored.

You can also just think through the problem. You constrained this label’s leading and trailing edges to resize with the superview. Then you constrained its width to a fixed value. These are conflicting constraints, and the solution is to remove one.

In BNRDetailViewController.xib, delete the width constraint that you just added to the label and set its background color back to clear.

Misplaced views

If a view’s frame in a XIB does not match its constraints, then you have a misplaced view problem. This means that the frame of that view at runtime will not match how it currently appears on the canvas. Let’s cause a misplaced view problem.

Select the label that displays the date and drag it down a little bit so that the interface looks like Figure 15.20.

Figure 15.20  A misplaced view

A misplaced view

A rectangle with an orange, dashed border will appear where the label used to be. This is the runtime frame; at runtime, the existing constraints will position the label where this rectangle is and not where you just dragged it to.

How you fix the problem depends on whether the view’s size and position on the canvas are what you want. If so, then you change the constraints to work with this new position. If not, then you change the view’s size or position to match the constraints. Let’s say that moving the label was an accident and that you want the view’s position to match the existing constraints.

Select the date label. Then, in the Auto Layout constraints menu, select the A misplaced view icon to reveal the Resolve Auto Layout Issues menu (Figure 15.21).

Figure 15.21  Resolve Auto Layout Issues menu

Resolve Auto Layout Issues menu

Select Update Frames at the top. This will reposition the label to match its constraints.

On the other hand, if you wanted the constraints to change to match the new position of the view, you would choose to Update Constraints.

The Resolve Auto Layout Issues menu is very powerful. Here is a description of the items in the top half of the menu.

Update Frames

Adjusts the frame of the view to match its constraints.

Update Constraints

Adjusts the constraints of the view to match its frame.

Add Missing Constraints

For views with an ambiguous layout, this will add the necessary constraints to remove the ambiguity. However, the new constraints might not be what you want, so make sure to double-check and test this resolution.

Reset to Suggested Constraints

This will remove any existing constraints from the view and add new constraints. These suggested constraints are sensitive to the context of the view. For example, if the view is near the top of its superview, the suggested constraints will probably pin it to the top, whereas if the view is near the bottom of its superview, it will probably be pinned to the bottom.

Clear Constraints

All constraints are removed. If no explicit constraints are added to this view, it will have the default fixed position and size constraints added to it.

The bottom section repeats these options but applies them to all of the subviews instead of only the selected view(s).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset