This workshop will be retired on May 31, 2020.
Debugging Auto Layout13:33 with Josh Timonen
In this workshop, we will demonstrate how to debug the two main issues you are likely to encounter with Auto Layout
[MUSIC] 0:00 Using auto layout can help you create smart, dynamic layouts. 0:04 In this workshop we're going to assume some familiarity with auto layout. 0:09 So if you're new to it, first check out the main Treehouse auto layout workshop 0:12 titled Using Auto Layout in iOS. 0:16 Today we'll demonstrate how to debug the two main issues you're likely to encounter 0:19 with Auto Layout. 0:23 First, we'll discuss unsatisfiable constraints. 0:24 This is when a view has conflicting constraints and 0:27 can't satisfy them all simultaneously. 0:30 We've basically created a game of musical chairs. 0:33 When the music stops, or rather at compile time, one of our constraints 0:36 isn't going to be applied, and we can't be certain which one it will be. 0:40 The compiler breaks the layout, removing constraints in the way it sees fit, 0:44 leaving you with a layout that probably wont look like what you're expecting. 0:48 These auto layout issues are actually the easiest to catch 0:52 because they'll be logged out in the console while running the app. 0:55 We'll take a look at this in a minute. 0:58 Second we'll discuss ambiguous layout, which is kind of the opposite case. 1:01 It's a little more difficult to identify but we'll show you how. 1:05 Ambiguous layout is what happens when the compiler has 1:09 too few constraints to draw the view with certainty. 1:12 The compiler is forced to pick values at random for the missing constraints. 1:15 And again, we're rolling the dice and could get a layout that's undesirable. 1:19 So lets take a look at a demo project with a few examples. 1:23 First, we'll create a single view project. 1:27 We'll call it debug auto layout. 1:34 [SOUND] In the ViewController.m, 1:37 we'll add a subview to it in ViewDidLoad. 1:41 We'll write UIView subview =UIView new. 1:47 Next is a very important step. 1:54 If you're going to be writing your auto layout in code, as we will here, 1:56 then usually you'll want to write subview.translatesAutoResizingMasksIntoCo- 2:00 nstraints = no. 2:05 iOS defaults to yes, which means that it converts a view's auto resizing masks 2:07 into constraints, as the name implies. 2:11 Forgetting to set this value to no is a common way for your views to end up with 2:14 extra unwanted constraints, likely causing unsatisfiable constraints. 2:18 Let's also set subview.backgroundColor = UIColor greenColor. 2:23 Then, we'll add the view as a subview with self.view addSubview: subview. 2:32 Next, we'll write our constraints. 2:39 We'll use visual format here since they'll be pretty simple. 2:41 We're going to pin our subview to its superviews edges with 30 2:44 points of padding. 2:48 We'll write self.view addConstraints, then build our array 2:49 of constraints with NSLayoutConstraint constraintsWithVisualFormat. 2:55 We'll add H to specify horizontal, |- 3:01 30 in parentheses to specify 30 points of padding. 3:06 Our subview in brackets, then 3:11 30 pixels of padding on the right side, and finally, the pipe. 3:16 We'll set options to zero, metrics nil, and 3:24 views NSDictionaryVariableBindings subview. 3:28 The last view's dictionary parameter is where you list the views 3:35 used in the visual format string. 3:39 Now let's copy and paste this line and 3:41 just change the H to a V to specify the vertical constraints. 3:43 Now if we launch our app in the simulator we'll see the view drawn as expected, 3:48 with 30 points of padding around the edges. 3:52 If we rotate in the simulator, with the command left or command right 3:54 arrow hotkey, we can see that the view is drawn as expected on rotation. 3:58 Great. 4:02 Lets go back to our auto layout, and specify a width for the sub view. 4:04 In our horizontal visual format string, after subview, 4:08 we'll add ==100 in parenthesis, which sets it to 100 points wide. 4:11 If we launch in the simulator again, 4:18 we'll see the console has dumped out a whole bunch of information. 4:20 Let's have a look. 4:23 The first line says, unable to simultaneously satisfy constraints. 4:31 And it says there's probably a constraint here we don't want. 4:35 It even has a note about if you see NSAutoResizingMaskLayoutConstraints 4:38 in the list below, then you may have forgotten to set 4:43 translatesAutoResizingMaskIntoConstraints to no as we discussed a few minutes ago. 4:46 Below that is our last of constraints that we couldn't make sense of. 4:53 They're all prefixed with H. 4:57 You can also see they're all referencing the same view. 5:00 These pieces actually look a lot like our visual format strings. 5:03 But basically they're broken up to their individual constraint components. 5:06 As you'll recall, the constraints with visual format method returns an array, so, 5:11 what we're seeing here are the individual constraints generated from this method. 5:15 The first one is the 30 points of padding between the super views left edge, 5:20 and our view. 5:24 The next is the 100 point width constraint on the view. 5:26 Next, is the 30 point padding on the right edge. 5:29 Finally, we have UI view encapsulated layout width set to 375. 5:33 But have a look at its hex address in memory written next to this view. 5:38 Its different than the other. 5:42 This is probably the view controller self.view or 5:43 the super view of our subview. 5:47 In other words, its saying that the super view is 375 points wide and 5:49 the math of these other constraints for the subview just don't add up. 5:53 Our example here is pretty straightforward but your views might be complicated. 5:58 We can use these hex addresses to right of each UI view in the log message to 6:02 match them to what we've written in the app. 6:06 Let's go back to the project and make that sub view a property. 6:09 We'll add it as property (nonatomic,strong) UIVew subview. 6:13 Then, set the view we created 6:21 with self.subview = subview. 6:26 [NOISE] Next, let's add a viewDidAppear method to this view controller, 6:31 just calling super within it, and let's add a break point here. 6:37 We'll run the app again. 6:43 And it will pause here. 6:44 We've already received the unsatisfiable constraints error, just as before. 6:46 But now that we're paused in viewDidAppear in this view controller, 6:52 we can print out properties that exist in this context, like our subview. 6:56 We can do this by writing po self.subview in the console. 7:00 We can now see it hex address which matches to the UI view that's causing 7:07 constraint problems. 7:11 We can also po self.view and see its hex address, which matches the UI 7:17 view encapsulated layout width constraint, which is also what we expected. 7:23 It even defines it as the pipe. 7:32 Or the super view of our sub view. 7:35 You can also look over to the variables view, to the left, to see these values. 7:40 If the filter setting below is set to auto, 7:45 you might want to change it to all variables. 7:47 You can also right click and print description of variables in the console. 7:50 So obviously the math doesn't add up for our horizontal constraints. 7:57 We're pinning to the edges of our super view which mean the entire width 8:01 is going to be whatever that number might be, in our case 375. 8:05 If we just have the 30 padding on each side, 8:08 the subview's width is calculated to be whatever is left. 8:12 In our demo case, that works out to 315. 8:16 But if we also say the width must equal 100, 8:19 than we have unsatisfiable constraints. 8:22 The simplest solution would be to remove the width equals 100 constraint. 8:26 But other solutions are possible. 8:30 For example, we could change the left padding to be greater than or equal to 30. 8:33 Let's try this and rerun the app. 8:42 As you can see, we no longer have conflicting constraints, and 8:46 the layout is different. 8:49 When we rotate, the bar stays pinned to the right adjusting the padding on 8:51 the left since we specified it as greater than or equal to 30. 8:54 Next, let's go back to our horizontal visual format string and 8:59 remove everything to the right of the subview right bracket. 9:03 [SOUND] We'll also remove the greater than or 9:06 equal from the left padding and 9:11 remove the 100 point width constraint on subview. 9:14 So now all we're really writing is a single horizontal constraint. 9:21 Pin subview to the super view's left edge with 30 points of padding. 9:25 If we run the app you'll notice that we don't even see the view. 9:30 This is likely a case of ambiguous layout. 9:34 We haven't specified enough constraints for 9:37 the compiler to draw our view with any certainty. 9:40 It's most likely just drawing our sub view with the width of zero, 9:43 since we never gave it a specific width and didn't pin it to the right edge. 9:46 We only specified the 30 point left padding. 9:50 In Xcode, hit the pause button next to the break point button. 9:54 This pauses the program. 9:59 In the console, we'll write po, again for print out, UIWindow 10:01 keyWindow_autoLayoutTrace and hit enter. 10:06 The debugger prints out our current view hierarchy starting at the key window 10:13 for our app. 10:17 Our app is pretty simple, just a UI window, 10:19 a UI view nested in it, which is our view controller's view, 10:21 then some layout guides, then another nested view which is our sub view. 10:25 To the right of its hex address, it says, ambiguous layout for 10:30 UI view, then a text address, then width. 10:33 Okay, so this is useful. 10:37 It's saying that it can't determine the width of our view. 10:39 Instead of width, you might see min x or 10:43 min y indicating other relevant positioning values on the view. 10:46 If we want to confirm that this is the view we think it is, 10:50 lets put our break point back in viewDidAppear, then launch the app. 10:53 We'll print out our auto-layout trace again which you can do easily by pushing 11:01 the up arrow then enter to get to the last command. 11:05 Then type po self.subview We can compare the hex addresses of our subview and 11:11 the view in the autolayout trace and see that they match. 11:18 So now we know we need to supply additional information about 11:22 the horizontal layout on this view so 11:25 that the compiler can determine its width properly. 11:27 We can pin the right edge to its superview or add a constant width to subview. 11:30 These two solutions will both remove the ambiguous layout but 11:35 each has a different result. 11:38 Let's give subview a constant width at 100 points with == 100 after subview. 11:40 Now lets remove our break point and relaunch the app. 11:47 We'll again pause while on our view, print out the auto layout trace, 11:54 and see that we no longer have ambiguous layout. 11:58 Great. 12:04 Here are some final thoughts. 12:06 To display without ambiguity, the general rule of thumb is that each 12:08 view needs two constraints vertically and two constraints horizontally. 12:12 If you isolate your thinking to just one axis at a time, 12:16 this starts to make a little more sense. 12:19 Let's just think horizontally for a second. 12:21 You can pin the left edge of something, and the right edge to something. 12:24 That would be two constraints. 12:28 The dynamic element is the width in this case or 12:30 you can pin to the left edge and set the width. 12:34 Again, two constraints and 12:37 in this case the right padding is calculated dynamically. 12:38 You could also pin the views center X to its superview center X for 12:42 the first constraint, then set its width. 12:46 That's two constraints, and the left and right padding is calculated dynamically. 12:49 You can even pin the center X to the superview center X, 12:55 then just set the left padding. 12:58 That one might take a little more thinking to get your brain around. 13:00 Since we're pinning the view's center and we have a distance from left 13:03 edge to center, the compiler can extrapolate its right half. 13:07 So at least two constraints per axis is a good sanity check when writing 13:11 your layout. 13:15 Just remember that if you have more than two, you should probably have some 13:16 other rules or conditional logic in there, such as greater than or 13:19 less than statements, to prevent unsatisfiable constraints. 13:23 Hopefully these auto-layout debug tips will help you keep those 13:26 UI designs happy and healthy. 13:29 Thanks for watching. 13:31
You need to sign up for Treehouse in order to download course files.Sign up