Get details about new features of Auto Layout in Xcode, Cocoa and Cocoa Touch. Explore the new NSGridView, allowing your Cocoa app to easily build grid interfaces. Dive deeper into new tools that enable you to quickly diagnose layout issues in your interface. Learn about the new features in Xcode to quickly build adaptive interfaces.
Good afternoon and welcome.
You want to create great apps.
And having a great layout is part of making a great app.
Auto Layout makes it easier for you to build your layouts,
targeting different devices, orientations,
adaptations as well as layout for different languages.
Today we are going to talk about what's new in Auto Layout,
and this session is broken up into three subparts.
First is going to be with me.
My name is Jason Yao.
I'm an Interface Builder Engineer.
I'm going to tell you what's an Interface Builder.
Second, Jesse Donaldson from AppKit will tell you what's new
in Cocoa, and then third, Marian Goldeen
from UIKit will tell you new debugging techniques.
With these tools and techniques,
you will save yourself developer time and effort
when you apply them to your workflow.
So, let's get started shall we?
The first topic I want to talk about is something
that I'm calling incrementally adopting Auto Layout,
incrementally adopting Auto Layout.
What do I mean by this?
Well, when you are laying out your views inside
of Interface Builder, you do not need
to constrain everything at once.
You can do it incrementally to save yourself time,
simplify your setups, as well as give yourself more flexibility.
But before I show you how to do this, let's first take a look
at an example to set the backdrop.
So it all starts by taking a view and dragging it
onto Interface Builder Canvas.
You give it a size and position.
You compile it and run.
And you're going to get that view as sized on the device.
Then you go head and rotate the device, and you realize
that it's fitting the same size but there is some work
that needs to be done.
So what's going on here?
What's going on is we're simply pinning this to the top
and to the left with the width and height.
In fact, what we're doing is we are implicitly creating
constraints during compile time so that this view holds the size
in -- with the Auto Layout Engine.
If you needed more dynamic resize behavior,
what you would need to do is go to Interface Builder Canvas
and add your own constraints.
And the question is could there be a better way of doing this,
perhaps a way without constraints,
to get simple resize behavior?
There can be.
New to Xcode 8 is the ability in Auto Layout documents to be able
to specify autoresizing masks on your views
that do not have constraints.
What that means is that you can get simple resize behavior
by specifying autoresizing masks.
For those of you who remember a time before Auto Layout you
might recognize this UI.
This is the Springs & Struts UI.
You can pin things to the edges as well
as decide whether it's going to be width or height resizable.
And these views are going to work great next
to your constraint views.
In fact, what we are going to do is we're going
to take those autoresizing masks and we are going
to translate them into constraints.
But we are going to translate them
into constraints at runtime.
And there is a key distinction there.
We're generating them at runtime and not at build time
because it's going to be more flexible and more transparent
to you, giving you greater control if you need
to do anything programmatic to these views, because for those
who are pro-Auto Layout users out there,
you might recognize this flag.
On the view, we are simply setting
translatesAutoresizingMask IntoConstraints equals true.
What about for the views that you've added constraints
to inside of Interface Builder?
They're going to be the same as before.
You click on the view.
You are going to see all of the constraints
that are affecting that view.
Autoresizing masks are going to be ignored
and translatesAutoresizingMask for these views will be false.
So, putting all of this together back to the original problem,
we can incrementally adopt Auto Layout by first starting
with autoresizing masks to so the simple resize behavior,
and then add constraints for the more sophisticated behavior.
And that gives you greater flexibility.
When you are ready to add constraints, choose the subtree
that you want to add constraints to,
start from the parent, and work your way in.
This allows you to adopt Auto Layout on your terms
and is great for new layouts that you are going
to be building to get that -- the simple resize behavior,
but it's also great for documents
where you've been wanting to layout.
Well, now you can while preserving your
I'd like to show you a demo of this in action.
So, let's take a look at what this is.
So, I've got Xcode 8 open with Interface Builder.
I am going to build a weather app.
I've got my views and I've dragged them
into the View Controller,
and they are fitting to an iPad portrait.
And so, let's take a look at what it's going to look
like in landscape by going to the new device configuration bar
and switching it to landscape.
Clicking over here, you can see there's work to do.
So, let's go ahead and start with the top banner.
And we want this to be stretched across the superview.
Instead of adding constraints, I haven't added any constraints
to this yet, I can go into the size inspector.
And of course we've got the Autoresizing Mask Inspector
over here, and all I need to do is make it width resizable
and pin to the other edge.
Now, I want to work on the children.
I'm going to go to the moon, and by default this is pinned
to the top and to the left, the way I want it.
As for the cloud, I'm going to pin it to the right and get rid
of the pin for -- to the left.
And then for the label, I want this to proportionally resize
and proportionally precision.
So, I'll get rid of the pin to the left,
make it width resizable, and you can see there is a little
animation showing me the behavior it's going to be.
And then we'll just pin it to the top and bottom
and make it height resizable as well.
Now when we take a look at this thing, we can switch it
to landscape, and it's looking pretty good.
Let's try two-thirds split screen.
It's almost there, but my label is truncated.
What's going on there?
What's going on is as you take a look
at the label it's doing the right thing.
It's using autoresizing mask,
and it's proportionally resizing based on its superview.
Autoresizing masks do not take
into account the content size of your view.
I can fix this up by using constraints,
but for this particular case UI label, I do have another trick.
I'm just going to go into the Attributes Inspector
and then switch it from a fixed font size
over to a minimum font size.
That way it's fitting into the frame that it's given.
So, now when we go back to landscape full screen,
it's looking pretty good, as well as portrait.
Next, we are going to deal
with the temperature control in the middle.
I want a more sophisticated alignment for this.
So, I'm going to use constraints.
What I could do is I'm going to hold down --
I want to constrain the 75 to the superview.
So, I'm going to hold down control on the keyboard,
drag out a connection to the superview.
It brings up the constraint menu where I can hold down shift
and add multiple constraints at once.
So, I want to center it horizontally and vertically
and hit add constraints.
Similarly, I want to do the same.
Control drag to the sunny, hold down shift,
and we want to horizontally space this as well
as add a baseline constraint.
And then do the same to the sun.
Control drag, hold down shift, horizontal spacing,
and this time center vertically.
Now, my views have the constraints that they need.
And we just need to update the frames
so that they are in position.
And so we are going to go to the Resolve Auto Layout Issues menu,
for the selected views and hit update frames.
Next, I want to make the temperature control even larger.
So, I'm going to click on that
and just increase this font size.
And you might notice that when I increase the font size,
because everything is already in place, Auto Layout is going
to automatically update my frames for me.
We do more automatic update frames for you in Xcode 8.
So, next, we want to have a nice back drop,
and this is a sunny day.
So, we're going to go ahead and add an image view.
From the object library, I'm going to go ahead and drag
out that image view, size it to my superview,
and then set as image, as well as choose its fill mode.
In this case, we probably want something like an aspect fill.
Now, we want to make sure
that this thing is sizing to its superview.
And we could add constraints, four of them,
and pin them to each side.
But for this simple resize behavior, we don't even need
to add constraints because we could go into the Size Inspector
and just use autoresizing masks and pin it to all edges,
making it width and height resizable.
Last but not least, let's go ahead and put this to the top
of my document outline.
So, it's in the back.
And then we're going to clear the background on my banner
because this is a visual effect for you,
and we now can see the nice translucency.
And then we'll test it out.
So, it's looking good -- looking good right now.
It's looking good in landscape, two-thirds split view --
two-thirds split view and portrait,
as well as iPhone 6s Plus.
And so that is how you can incrementally adopt Auto Layout.
The next topic I want to talk
about is a little more advanced topic.
And it is about mixing design and runtime constraints.
There are situations where you could run into this type
of thing where you are laying out your views inside
of Interface Builder and you're just adding constraints,
but you do not know all the constraints you're going to add
to views until runtime, maybe because it's based
on information like time of day or like data that you're loading
that only the run time app knows.
There are three tools and three examples I want to walk you
through for dealing with these type of situations.
The first is the use of placeholder constraints.
In this situation, I've got an image, and I want to go ahead
and center it in my device both horizontally and vertically,
also a little bit inside form the leading margin.
I also want the image to maintain its aspect ratio.
However, I don't know what that final image is going
to be, not until runtime.
And so, in order to assimilate the approximate size and layout
so I can see the Interface Builder, I can go ahead
and add a 4 by 3 aspect ratio,
approximately what I think it might be, and then mark it
as a place order constraint.
Therefore, it would be removed at build time.
When I actually get the image that I'm going to be setting
in runtime, I'll go ahead
and create the real aspect ratio constraint and apply it in.
For the second example, pretend you are creating a
Your custom control is going to drive off of something
like US -- UIView or NSview.
It's going to provide its own drawing, and it also is going
to manage its own content.
It may want to even provide its own size.
It could provide that size to the Auto Layout system
by specifying its intrinsic content size.
Interface Builder is not executing that code,
and so it doesn't know what your intrinsic content size is going
to be, but you can simulate the approximate size of it
by giving it reasonable values using the intrinsic
Therefore, you could also see how it is going
to look like in your layout.
Just remember for your custom controls, if you're going
to do this, that you do need
to provide its real intrinsic content size,
and you can do this
by overriding the intrinsic content size property
on your custom control.
The final example is the one that's new to Xcode,
and I'm leaving it for last
because it should also be the option of your last resort,
when you have exhausted your possibilities
for both placeholder constraints and design
and intrinsic content size placeholders.
And so, we're giving you the ability
to tune the ambiguity warning levels on a per view basis.
What that means is that for this view I want to go ahead
and center it on screen, vertically,
but that's all I know.
I don't know what horizontal position it's going to be
or what size it's going to be.
In fact Interface Builder is drawing this thing is red
because it's warning me that it doesn't have enough constraints
to be able to position it.
I know that I'm going to be adding this constraints later
on at runtime when I have all the information that I need
so I can go ahead and remove the clutter from the --
from my work space by going to the ambiguity setting
and switching it from verifying always to either verifying
for position or never verifying at all, and I'm promising
to myself I will add those constraints later on so
that the view holds its size before the first layout pass.
These are the tools for being able to work
with designer runtime constraints
when you've exhausted the possibilities
of constraining everything you can inside of Interface Builder.
Well, what we've seen is how to incrementally adopt out a layout
and how to play well with designer runtime constraints,
and we're ready for the second part of this session.
I'd like to invite up my colleague Jesse Donaldson
to talk about what's new in Cocoa.
Over to you, Jesse.
Today I'm going to tell you about NSGridView,
a new layout container that were providing on macOS.
So maintenance on constraints can be complicated even
when you have something as simple as this set
of checkboxes, and we've built in a stack view
to make it easier to build things like this.
And it's great anytime you need
to spread some items across a space.
But there's some kinds of layout that can still be difficult
to achieve like this, for example.
You can build this with stacks, but NSStackView isn't going
to help you align the content
across both the rows and the columns.
So that's why we've built in NSGridView.
It makes it easy to put the content
into an explicitly defined grid, and it will take care
of alignment for you across both axes.
So let's have a closer look.
I took this UI from our voiceover preferences,
and I've added these purple lines just
to help you see how it can fit into a grid.
NSGridView uses a couple of helper classes, NSGridRow
and NSGridColumn, to represent the rows and columns,
and by default they're sized automatically to their content.
You can also specify an explicit size, if that's what you need.
These also let you add some additional padding
if you need some extra space here and there in your grid,
and if you have some UI
that doesn't apply the current hardware configuration,
then they can be dynamically shown and hidden.
NSGridView uses a separate class, NSGridCell,
to represent the individual cells,
and the job of a cell is just to manage the layout
for a particular content view.
Cells also let you control the placement of the content
if the cell has a bunch of extra space in it.
And if you have some content that needs
to span the boundaries between cells, then they can be merged
as you might be familiar with from a spreadsheet program.
So, I've built a smaller version of this UI
that still has all the interesting pieces in it,
and I want to go through this a bit at a time
and show you how it's built.
So we intend to provide support in Interface Builder
for NSGridView, but until we have it, we're just going
to be working with the code.
What I've done here is make outlets in my nib file
for all the controls that I want to put into my grid,
and then whenever you specify a ContentView for NSGridView,
it will take care of moving that to be a subview
of the grid if needed.
This makes it really easy to allocate your grid view
at run time and then integrate it
into an existing view hierarchy.
There's a few different ways to construct grid views.
I think this is the easiest one.
All you need to do is specify a list
of the content views for each of the rows.
And let's get to a couple of things.
First, you don't have to worry about sizing the grid.
It'll take care of however many rows and columns are needed
for the content that you've specified.
Second, the code that you end
up with is roughly grid-shaped itself and is
at least highly correlated with the UI that you're specifying.
This makes it much easier for you or somebody else
to come back later and find your way around the code.
So, if we just run this, this is what we get.
That's not too bad just for calling the constructor,
but it doesn't match the design yet, obviously.
The most striking problem is just
that the UI is very spread out.
There are all these gaps, like this one.
And this occurs because the grid view is constrained
to the edges of the window.
Now when I did that, my intent was
to make the window the same size as the grid,
but what actually happened was
that the grid was stretched to fill the window.
If you've used Auto Layout very much in the past,
you may have encountered similar problems
with text fields or other controls.
And we're going to resolve this here the same way,
by adjusting the content hugging priority of the grid view.
So, the other controls in this window already have a higher
content hugging from the nib file
but until we raise the content hugging on the grid view,
then it's possible for the window
to actually pull the content view edges away from the edges
from the rows and columns.
Once we raise it, instead we'll see that the edges
of the windows are pulled in, and the gap closes up.
So, the next thing I'd like to look at is these labels.
They're clearly laid out within theirselves,
but they really need to be right justified so that they're
up against the controls that they are labeling.
And this is easy to change
by just adjusting the placement value for those cells.
The x and y placement properties are available on rows, columns,
cells, and the grid view itself.
The idea is that if the value is not specified
on the cell itself, then the value from the row
and column will be used or from the grid view if necessary.
This makes it very easy to just set the value in one place
and then have it affect a wide range of cells.
So, in this case, we can get the column at index zero
from the grid view and just set the x placement on that column,
and we'll see both of the labels hop over to the right side.
The next thing I'd like to look
at here is the baseline alignment.
The text of the labels is not properly aligned
with the text in the controls.
And it's subtle in the slide, but it's easy to fix
by adjusting the row alignment.
Row alignment is inherited the same way as the placement,
and for this particular design,
it's fine to just have everything
in the grid align by baseline.
And so, we can set the value in one place on the grid view,
and it will affect everything.
And if you look carefully,
you'll see the text slide into place.
Now, one thing to keep in mind about row alignment is
that you may have a bunch of views
in your row that are all aligned.
And then you may also specify placement for those cells.
So, in that kind of a case, the grid view may not be able
to satisfy all the requests.
And so, it's important to understand
that the row alignment will always take precedence
over the placement.
The way it works is that we'll take the whole group
of aligned content views
and then we'll place it using the placement
from the first cell.
So, the next piece I'd like to look at is this pop-up button.
The design has a little bit of extra space above
and below the pop-up button, and we can get that space
in our layout by adding some padding to that row.
So, the first thing we need to do is fetch the row.
We could actually do this the same way
that we got the column before by specifying the index,
but this way is a little bit better.
Instead, we ask the grid view for the cell
that contains this pop-up button,
and then from the cell, we get the row.
This is better because if someone comes along later
and changes the configuration of the grid view to add a checkbox
or something like that, this code will still be valid.
If you fill your code with a lot of hard-coded index values,
then as soon as someone adds a checkbox, you need to go
and review all of that code to see
which of the indices need to be updated.
And in any case, once we have the row, we can go ahead
and set the padding values, and we'll see
that we get a little bit of extra space
above and below the pop-up.
We need a little space over the status cells label as well,
and we can achieve that the same way.
So, I want to take a moment here to talk about the difference
between padding and spacing.
We haven't really talked about spacing much.
The padding values are available on rows and columns,
and they are just for adding an extra space here and there
where you need it in your grid.
The spacing values are available on the grid view itself,
and they apply to the spaces
between all the rows or all the columns.
So, if we take a copy of the design here and we remove all
of the padding, then this is what we get.
The UI is still properly spaced out,
but we lose the visual distinction
between the different clusters of controls.
If instead we keep the padding but we take
out all the spacing, we get this.
We still have our control clusters,
but the UI is very cramped together.
And of course if you remove both, you end up with this,
where the whole thing is just very tightly compressed.
So, an important thing to keep in mind here is
that the padding properties all default to zero.
You won't have any padding in your grid unless you specify it.
But you almost always want some space between the content views,
and so the spacing on the grid view defaults to non-zero.
If your use case requires that your views are laid
out immediately next to each other, then you'll need to go
to the grid view and set those spacing properties to zero.
The last piece that's really out of place here is this checkbox,
and it's an interesting case because it needs
to span the boundary between these bottom two cells.
But we can do that by merging the cells,
like I mentioned in the introduction.
There are a few methods for this, but in this case,
we can just tell the row to merge its first two cells.
And when you do this, it has the effect of extending the boundary
of the top leading cell to cover the entire merge range.
So with this code in effect we see the checkbox slides,
and now this content view is laid out across both cells
as if they were the same one.
And in fact, you can see it's still inheriting the trailing
placement from that first column.
We don't really want it to have trailing placement,
but we don't want to have leading placement either.
This checkbox is actually supposed to be centered
on the boundary between the two columns.
So, because the columns are not evenly sized,
this isn't something that the grid view will do
for you out of the box.
But it does leave you room to do it for yourself.
And that starts by setting a placement for this cell to none.
When you set these placement values to none,
it has the effect of causing the grid view to stop maintaining
that aspect of the layout.
And what that buys us is
that the grid view won't be applying any constraints
that might conflict with our constraints.
So, once that's done, we can go ahead and make a constraint
from the checkbox's center x anchor to the leading anchor
of the checkbox above it.
And then once we have the constraint,
we could actually just activate it
like you normally would with a constraint.
But in this case, we're going to set it
in the custom placement constraints array.
If you do that, then it allows the grid view
to maintain any custom placement constraints for you,
and it will do things like activate
and deactivate the constraints depending on whether
or not the cell is visible.
So, with this code into place, we see the checkbox shift
into position, and the implementation then matches
Some pieces of the grid configuration here were a little
bit complicated, but the whole thing is still much simpler
that if you needed to build this UI
from basic constraints or even with stacks.
In fact, there's not a lot that needs to change in order
to expand this to the full UI that I swiped
from the voiceover preferences.
So, to summarize, NSGridView is great
if your application has a static grid-like UI
that you need to manage.
And a great way to start working with it is to take all
of your content views and put them
into a grid and see what you get.
At that point, you can iterate on the configuration
of your grid until you've reached the layout design
that you're looking for.
I hope you find it useful.
Next, I'd like to bring out my colleague Marian Goldeen,
who's going to talk to you
about some new debugging tools that she's built.
It doesn't happen often, but when it does it's a hang,
and then that turns into a crash,
and it can be hard to debug.
And that's a layout feedback loop.
When you encounter a layout feedback loop,
you're usually beginning or ending a transition,
and it might be something like this.
You tap the button to start a transition,
and the button responds, but then nothing else does.
So, you're running on Xcode, and you look
in the debugging navigator, and you see that the CPU is pegged,
memory's increasing, and maybe you break and you look
and you just see a bunch of layout in the backtrace.
And what's happening is that there is some collection
of views that are running layout again, and again,
and again in a tight loop.
The run loop's never turning.
Any messages that are happening are collecting
to autorelease objects, so that's all collecting.
So that's why the memory's increasing.
And what's driving this is an upstream setNeedsLayout.
And what I mean by that is one
of the [inaudible] views during its layout is doing something
that is causing a more root directed view,
to get a setNeedsLayout so when layout finishes its pass,
it goes right back to the top and starts again,
and what you want to know is what views are involved
because that's going to help you figure out where
that setNeedsLayout is coming from, why,
and what you're going to do about it.
This information is actually a little difficult to collect.
So, that's why we're introducing a layout feedback loop debugger
to help with these particular situations.
This is a launch argument that you add in Xcode.
It's the UIView
or NSViewLayoutFeedbackLoop DebuggingThreshold,
depending on whether you're on macOS or iOS.
And you give it a value.
I've used 100 here.
You can use any value, but we'll clamp it between 50 and 1,000.
Now when you've got this launch argument set,
the layout feedback loop debugger will be counting layout
subviews for every view that runs layout,
and if any of them run more than that threshold
within the same turn of the run loop,
it will then allow the cycle to continue
for a little bit while it collects information.
Then it will throw -- raise an exception and dump
that information into the logs.
It dumps it to the com.apple.AppKit subsystem
or the com.apple.UIKit subsystem,
depending on whether you're on macOS or iOS,
in both cases the layout loop category.
If you want to know more about the new logging subsystems
and categories, come to the session at five o'clock today
at Knob Hill on the unified logging and activity tracing.
Alternatively, you can set a break point in the debugger,
an exception break point.
And you can print the feedback loop in the debugger,
or it's also nice to hit the break point
and maybe introspect what's going on a little more.
So, I said info dump, and I do mean info dump.
So we're going to go through two examples
of real live layout feedback loops that I debugged
with the layout feedback loop debugger.
Now we're going to look at those logs, warts and all,
and hopefully a walkthrough
of two different logs are going to help you.
We're going to look at a layout feedback loop that was caused
by an upstream setNeedsLayout.
This is actually independent of Auto Layout, and another one
that was caused by ambiguous layout from constraints,
which is very special to Auto Layout.
So for the first example with the upstream geometry change,
here's a graph of the view tree
where the feedback loop was happening.
And a lot of view in that view tree were actually
Often you're lucky and there aren't so many views,
and ones like this your heart kind of sinks.
But it turned out that ten of them were just churn.
They were innocent victims of the actual problem
that was happening higher up in the hierarchy.
So, what was going on was that that third level view,
during its layout, was changing the bounds of its superview.
Now when a views bounds change,
it gets an implicit setNeedsLayout
because it will need
to re position its views for the new bounds.
But also if that view that's receiving the bounds change,
if its superview is not actually in layout,
the superview will also receive a setNeedsLayout
so that layout subviews will have the last word
on what the layout is.
So when the layout pass gets down to the bottom and finishes,
it goes back to the top, and it runs layout on it
because that top view still needs layout,
which resets the bounds of the middle view.
And the feedback loop is powered by the two views fighting
over the bounds of the view in the middle.
As you can imagine, there's a lot of layout going on.
So, there's a lot of information in the log.
So brace yourself.
This is what the log looks like, or at least the top of the log,
which is where I want you to start
when you look at one of these logs.
The first thing that's called out is the top-level view
that -- of the layout feedback loops.
So, in this loop there is no view more root word
than this view that's getting layout.
Following that is a recursive description of the subtree
under that top-level view.
And next to some of the views
in that description, you'll see a number.
For instance, the -- you will always see one next
to the top-level view.
In this case it's 23.
Those numbers, the ones with the numbers are the views
that are actually receiving layout,
and the numbers are in ordering.
Of course, it's a cycle, so we can put one anywhere.
But we do it so that the last one is the top-level view,
so it gives you an idea of how many views are involved.
So, that's a 23, but there is only ten down here,
and there is three up here, and that doesn't add up to 23.
So, what happened?
Well, let's go look at the next section of the log,
which is the views receiving layout in order.
And in this section, you can see that just
because there is one loop doesn't mean that every view
that lays out in lays out only once.
We have ten views that are laying out,
followed by another two views,
followed by those same ten views again.
And so, those where the more [inaudible] views.
And when you see this situation, where you have a bunch of views
that lay out more than once in the cycle, they're often victims
of the other ones that are more important.
So, as I said early on, we're really interested
in where is the top-level view getting its setNeedsLayout from.
So, following this section is a lot of detail
about the actual --
what's actually happening during the layout.
So, we're going to scroll past that down to the bottom
of the log to a section called call stacks sent
to the top-level view.
So, it's down there, and you just have to look for it.
Now, there can be more than one of these.
You know, usually there's only one.
This was a pretty refractory one, and there were several.
They're pretty similar.
So, I'm only going to show you one of them.
At the top of the backtrace are some funnel methods
for the feedback loop debugger, but pretty near the top you see
that in frame five, you see the DropShadowViews receiving a
And if you remember from the recursive description,
the DropShadowView was the subview of the TransitionView.
So, the only way that set bounds
of the DropShadowView could be causing a setNeedsLayout
on the TransitionView would be
because the TransitionView is not in layout.
So, that view in frame seven that's running its layout is not
It's something else.
But we don't have that information in this backtrace.
I didn't have symbols for this particular app.
So there, they are not showing up.
Furthermore, it could be a view.
We're lucky here because it's the DropShadowView that's
receiving set bounds, which override set bounds,
but it could be a view doesn't override set bounds.
But hopefully between the information from the backtrace
and the information from the top, you'll know what
in the details you're interested in.
And so, what we are interested in is the frame changes
for the DropShadowView.
So, we scroll back up,
and we find where that one -- information is.
So, this is under some geometry change information.
And we see that in fact these geometry changes
to bounds changes and our frame changes --
change are repeating again and again.
And two of those changes are coming during layout
on the TransitionView, which is rational and what you'd expect.
But one of them is coming from the viewLayoutSubviews method
of a particular view controller for the view that's a subview
of the TransitionView.
So, we've located our problem, and the way to fix this bug is
for the programmer to figure out some other way
to achieve their ends that don't involve changing the bounds
of a superview during layout.
All right, so now we're taking a breath because we're going
to example two, which is a different kind
of feedback loop entirely.
For those of you who have been using Auto Layout
to do anything very complicated, you've probably run
into the problem of having ambiguous layout.
And ambiguous layout is usually not so terrible.
Usually you get maybe a bunch of zero size views.
And you're like where did my views go, or maybe the view --
the layout is what you want except occasionally you rotate
the device or something and you get a different layout.
But sometimes you're really unfortunate,
and during the update constraints passes
that precede layout, you can exercise that ambiguity.
And if that ambiguity gets exercised,
then variable changes will occur with each one,
and each time it'll dirty layout somewhere.
And so you get this cycle.
Now an ambiguous layout as a layout feedback loop leads
to something that's really
that gets you scratching your head unless you think of it.
So, that's why when this is likely,
we call it out at the top of the log.
So they're already thinking about it.
And so, it says ambiguous layout is suspected.
And then when you look at the recursive description,
you see ambiguous layout all over the place, and, you know,
it's pretty suspicious.
I'm going to digress here a moment
because I have abbreviated things in these logs
to make them fit okay on the slides.
And -- but there's an abbreviation in there
that you might not recognize
that is actually in the logs itself.
And that's the tAMIC.
You see that right there were it says ambiguous layout.
So tAMIC stands for Translates Auto Resizing Mask
into Constraints, and now you can all call it that too.
At any rate, so were pretty sure
that ambiguous layout is our issue.
And we could go look at the backtrace
for the setNeedsLayout is set to the top-level view.
But I'm not going to show you that.
All that's in there is internal foundation and UIKit methods.
And it's not helpful to us.
So, in this case, we need to look at the details,
and the details here are going to tell us views
with variable changes that are triggering layout.
And one thing you need to remember
about ambiguous layout is it's contagious.
So, you might just be missing a couple of constraints.
But a bunch of views are ambiguous
because they're all kind of dependent on each other.
So, you may have a lot of these, but you just start with one
and once that's fixed up,
a whole lot of the rest will evaporate if not all of them.
So you look at this.
You see that the min x variable is oscillating between minus 120
and minus 160, which is kind of odd values anyway.
And because it's ambiguous layout, we list the constraints
that are affecting the layout,
which you can then hopefully examine
and get some idea of what's missing.
I'm going to go over now examining constraints
because it can be intimidating to see a list of constraints.
And drawing pictures is the only way to deal with it.
I like to start with a graph of the view hierarchy of the views
that are in -- that are listed in the constraints.
And fortunately for this example,
the views were all different subclasses
so I could label them for you.
And the next thing I drew is draw myself a picture
of what the constraints were.
So the constraints were a minimum leading
and trailing padding for the label inside the container.
And there was a centering constraint
between the container and the action view.
And then the action view had autoresizing mask constraints
positioning it within the representation view,
kind of positioning it in a weird place.
Something's definitely funky with these constraints.
And finally there's an alignment
between the representation view and its sibling.
But there's nothing that actually insists that these --
this whole view hierarchy needs to be in any particular place.
So, that's how we got this ambiguity
and the layout feedback loop.
So that's layout feedback loop debugger.
It's a launch argument.
You won't need to use often.
But when you do, it should save you a lot of time.
And to recap, we saw the --
how you can incrementally adopt Auto Layout
and Interface Builder.
In AppKit, there's NSGridView for grid-like layout.
If you'd like to see that soon on iOS, cast your vote
in Apple Bug Reporter.
Finally, we have the layout feedback loop
More information on the Dub-Dub website, and have a great rest
of the afternoon, and thanks for coming.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.