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