Transcript
[ Music ]
[ Applause ]
>> Welcome.
My name is Steve Breen.
I'm engineer on the UIKit team.
And I'm joined today by me
colleagues, Troy Stephens from
the AppKit team; and Dersu
Abolfathi [phonetic] from the
App Store team.
So today we're going to talk a
little bit about advances in
CollectionView layouts.
Now, we're going to break the
talk down into four broad
segments.
First we're going to go over the
current state-of-the-art -- how
do we do this today?
How do we define CollectionView
layout in our applications?
Then we're going to go over a
brand new approach that we're
bringing to all the platforms
this year -- iOS, tvOS, and the
Mac.
And then we're going to go on to
some hands-on demos so you can
see this API in action.
And then we'll do a tour of some
more advanced features of how
you can get the most out of this
API.
All right, we got a ton of
content.
So let's get started.
All right.
So first let's talk a bit about
the current state of UI.
What do we do today?
How do we define CollectionView
layout?
Now, when CollectionView was
introduced back in iOS 6, it has
a really novel concept; it had a
separate abstraction for
defining layout.
So it's really two classes
acting in concert with each
other, where one does a
rendering and one is responsible
for where things go or the
CollectionView layout.
Now, CollectionView layout is an
abstract thing.
So we have to subclass to get
use out of it.
And we shipped a concrete layout
class with iOS 6 called
CollectionView flow layout.
Now, flow layout was really
useful for a ton of different
designs, especially back in the
iOS 6 days where things were
maybe a little bit simpler.
And it did this because it uses
a line-based layout system.
Now, we covered this last year
in a tour of UI CollectionView.
But in summary, a line-based
system allows you to lay out on
the orthogonal axis, of the
layout axis, until you just fill
up the amount of space you have
available and we drop the next
line.
And this is really great, it
works simple, easy to reason
about.
You can get it up and running
pretty quick.
But what about today's apps?
So as devices got more
heterogeneous and screen sizes
got different, things get mother
complex in today's apps.
And here we see the App Store
that we're shipping in iOS 13.
Now, if your designer handed you
this design today, what would
you do?
Well, you'd think, "All right, I
got to choose how I'm going to
design this thing."
And there's a lot of options now
more than never.
And you might settle on
CollectionView.
And if you did that, you would
think to yourself, "Well, can I
really make flow happen here?"
And you'd probably discard that
right away.
So at that point now you're
facing a custom layout.
And, you know, I see what you
guys say on Twitter about custom
layouts.
And they can be complicated.
So last year we talked about
this in a tour of
CollectionView.
And we talked about what's
involved with building a custom
layout.
Now, there's a certain amount of
stuff you have to provide in
your concrete layout class, and
we walked through those things.
And we also walked through the
performance considerations you
had to think about when you're
designing a custom layout to
make sure it's fast if you have
a large number of items in your
CollectionView.
But we didn't cover everything,
and there's additional
challenges if you're building
these custom layouts.
And there's a number of those,
but I'll cover a couple here,
you know, supplementaries and
decoration views -- two of the
view types that you can manage
in CollectionView -- are a
little tricky in custom layout.
You're on your own.
And there's also self-sizing
challenges you have to wrestle
with.
And we'll get into that more
later.
So this year we're bringing a
brand-new concrete layout class
to the platforms that sits right
alongside flow as a peer.
And we call this compositional
layout.
Okay. So --
[ Applause ]
Haven't even seen it yet.
So -- so what is compositional
layout?
What does this thing do?
Well, just a little philosophy
right up front.
Hey, what is this built on?
It has three basic tent poles
that we built compositional
layout on.
First, it's composable -- the
idea of making complex things
from simple things.
And it's designed to be
flexible.
You can write any layout with
compositional layout.
It's extremely flexible.
And it's also fast by default.
So we've taken all of the
performance optimizations on
ourselves in the framework so
you don't have to think about
it.
Compositional layout's all about
describing or defining what you
want to do.
It's a declarative kind of API.
All right, so composing --
you're going to hear this word a
lot in this conference.
So how do we do that with this
compositional layout idea?
Well, it's all about taking
small bits of layout, these
little components, and stitching
them all together.
So you're composing larger
layouts from smaller bits of
layout.
And we've learned from the great
lessons of flow layout where we
bring some of those line-based
lessons where you can lay out
along a line.
We may now how many items there
are, we may not.
But we can lay out items on a
line in these little layout
groups.
And finally, as the name
implies, you don't subclass.
You literally just create a
thing and compose some elements
and then -- and then you're good
to go.
All right.
So that's a lot of talk for no
code.
And we're all about code at a
conference like this, right?
So let's go look at some code.
All right.
So this is a hello, world
compositional layout.
There's five lines of code.
Now, I'm going to switch over to
my device over here.
All right.
So here we see our example app.
And I'm going to go to the list
example.
Wow, that's a boring layout.
Okay, so this looks like a
table, right?
Here we go.
Not much to that.
All right.
So let's focus strictly on the
code for a second.
Let's look at this.
All right.
So there's a couple observations
that will jump out right away.
And the first observation
because I know developers is,
like, "Hey, Steve, I can write
this in two lines of code with
flow.
What is all this nonsense?"
And it's absolutely true, you
probably could do something like
that.
But what I want you to anchor in
your mind and think about
throughout the presentation is
that the amount of code as these
layouts get more and more
complex does not grow linear to
the problem size, it just kind
of tapers off.
Because we're just going to
compose in new things to the
layout in very simple,
easy-to-reason about ways to get
very complex layouts.
I'm super excited to show you
this stuff.
The second observation is I want
you to look at the -- there's a
natural progression here of
these types, right?
We got, like, you know, five
types here.
It's kind of -- what's up here?
And the types I want to focus on
first are these four types, and
they have this natural
abstraction where they climb
this ladder.
And we start with an item which
gets folded into a group.
And the group goes into a
section.
And the section goes in our
layout.
Now, let's look at this
visually.
All right.
So here, this big rectangle is
the layout.
And the layout is your entire
layout.
And now we have these blinding
white guys that represent our
sections, right?
And this maps directly onto the
data sources, you know, content
for those sections.
And then we're going to
represent a kind of a grid-style
traditional layout in this
particular example.
We see these groups which
represent our rows.
All right.
So inside that we have items.
So this is basically just
showing off this hierarchy we're
going to see all throughout the
talk, this repeating pattern of
item, group, section, layout.
Okay. So now I want to do a
little bit of talking about some
of the concepts, these core
types in compositional layout.
And once we get through all
this, we can jump over to the
demos to see how it all fits.
So I want to start with talking
about sizing.
So compositional layout has
extended sizing to make it
really easy to reason about how
you size things inside of a
compositional layout.
And everything has an explicit
size, has a strong opinion about
how big it is.
Now, we're in this Euclidian 2D
geometry plane, all right, with
flexion view.
And as such, a size really is
just two properties.
It's a width and a height
dimension.
And here we can see we've got a
stripped-down version of that
type definition.
And it has a width and a height
dimension.
But notice that the width and
the height dimensions, they're
not -- they're not scalar
values.
It's not just a float or
something, it's actually another
type, this
NSCollectionLayoutDimension.
All right.
So what's that?
It's really simple.
This is an axis-independent way
to describe how big a particular
axis is.
And we have four different
variations of how to define this
thing.
And let's walk through these in
a kind of a visual way.
So let's say you have an item
and you want to describe its
size relative to its container.
So the outer-most container will
be your CollectionView.
Here we would just say, hey,
this item's widthDimension is
going to be a fractional width
or 50% of the width of its
container.
And similarly, we can say the
height of something is a
fractional height of its
container, in this case 30%.
Now, because you can specify
things in this axis-independent
way, we can define something as
having a specific aspect ratio,
in this case an aspect ratio of
1, by defining both the width
and the height as a dimension, a
fraction of the width of its
container.
And we say it's 25% as its
container, and the height
mirrors this.
Okay. So those are the
fractional variants creating a
dimension.
What about point-based values?
Well, we have two.
First one is the simplest,
absolute.
You know you need this thing to
be 200 points because your
designer's emphatic it's got to
be 200 points.
That's what it is, right?
Here we go, we've got an
interesting concept, estimated.
So if you don't know exactly how
big an item is going to be --
and we're going to talk about
this a lot -- you can estimate
it.
Say it's 200 points.
And then over time, it grows as
the item renders and we know a
little bit more about the
content in that item.
All right.
So that's layout dimension and
size.
Next up let's talk a little bit
about item.
This is very simple.
This is a cell or it's a
supplementary.
It's a thing that renders on
screen.
And there's more stuff here
you'll see in the STK, but it's
a taste of that type definition.
We see that when we construct
one of these things, we give it
a size.
Everything's got an opinion
about size.
All right.
So we're going to continue up
that abstraction hierarchy.
We got item, and now we're in
group.
And what's a group?
A group is -- it's the
workhorse.
This is your basic unit of
layout that you're going to
compose together.
We have three forms: We have
horizontal, vertical.
You can think of these as, like,
little mini flow layouts, right?
They lay out on a line on the
horizontal axis and vertical
axis.
But then remember we talked
about before that it's flexible.
Well, if you have something that
doesn't lay along a the line and
there's a lot of layouts that
don't do that.
We have a custom group.
So what is this?
That allows you to specify the
absolute size and position of
items in a custom way.
So if you have something where
you have a pre-defined generator
that generates layouts, you can
use a custom group.
Or if you're doing a radial
layout and you want to compute
that, you can do it with a
custom group.
And what's cool is you can
compose custom groups right
alongside vertical and
horizontal.
So we build up the complex from
the simple.
All right.
So that's group, the workhorse.
Next up we have
NSCollectionLayoutSection.
This is just as the name
implies, this is the layout
definition on a per-section
basis of the CollectionView,
maps directly onto the data
source notion of how many items
are in that section.
And we can see, as promised,
that when the initializer here
takes in allow group.
So we've gone from item, group,
section.
All right.
Our final stop on this traversal
of stuff, these are the two
top-level layout classes, right?
So for iOS and tvOS, we had UI
CollectionView
CompositionalLayout.
And on the Mac we have
NSViewCompositionalLayout.
Now, what's interesting here is
the definitions for all these
things are the same, regardless
of platform.
There's just some minor
differences in the top-level
classes.
All right.
And the final thing to note
here, and this is actually
really interesting, which we'll
see a little bit more in the
demos, is the way you construct
a compositional layout.
There's two ways.
And the simplest way is just
specify a layout section's
definition.
So this is very similar to what
we do with flow today, right?
Because flow weighs out every
section like every other
section.
It's homogeneous in that way.
The compositional layout extends
this idea.
Because now we have this great
definition for what a section
is, you can specify a closure
that will be called back and
will ask for those definitions
for the sections on a
per-section basis.
Now, this opens up a lot of
possibilities because now your
layouts can be completely
distinct between sections.
And we have a lot to show you in
the demos of this later on.
And hey, later on is right now.
So enough nattling on about
types, we're going to go see
this in action.
And to do that, I'm going to
bring up my colleague, Troy
Stephens.
Troy?
[ Applause ]
>> Troy Stephens: Thank you,
Steve.
As promised, we're going to look
at some code.
We're going to dig right into
the practical mechanics of how
you can build just about any
kind of layout you can dream up
from these simple elements that
Steve just described.
So make sure to download the
sample project for this talk if
you haven't already.
That way you can follow along,
study it at your leisure, and
most importantly, use our code
freely in your own projects.
Whatever kind of layout you
might be thinking about
implementing, there's likely to
be an example we're going to
look at today that is similar.
By taking our code as a basis
for yours, you're going to be
that much closer to your goal
that much more quickly.
We're going to see that it's
really easy to take any existing
compositional layout description
and incrementally refine it to
be exactly what you want.
Now, as we walk through our
examples today, I want to you
notice the same basic pattern at
work in each of them.
This is the pattern that Steve
introduced us to.
Every single compositional
layout description is composed
of the same four basic parts:
Item, group, and section
descriptions wrapped in the
overall containing layout.
So in each of the code examples
we're going to look at, we're
going to see that same
four-layer nested structure.
And in the more advanced topics,
we're going to see how you can
actually nest groups inside of
other groups in order to
construct or compose more
complex, sophisticated layouts
out of simple, easy to
understand parts.
So let's get to the code.
And here we have -- we're going
to start with our list example,
which is the one that Steve
showed us.
So here's -- this is about the
simplest kind of useful layout
we can imagine.
It's a single-column list where
the items span the width of our
CollectionView.
And if I rotate the phone, we
can see that indeed the items
expand to fill the available
width while maintaining the same
constant height.
So how do we implement this
using compositional layout?
I've got our list view
controller source file open
here, which is the one where
there is implemented.
And in each of our examples
today, we've just gone ahead and
declared a create layout
function that neatly
encapsulates our description of
our compositional layout that we
then return back out to be
hooked up to our CollectionView
just like any other
CollectionView layout would be.
So first thing to notice here,
we start, as I promised, by
describing an item and itself
size.
We take that item description
and we use it to describe a
group of items.
Next, we wrap that group in a
section.
And finally, we create and
return our compositional layout.
So there's that item, group,
section layout structure.
The other thing I want you to
pay attention to here and we
should really understand before
we move on to the other examples
is the way that the items are
given their size.
In this case it is the group
size that ultimately ends up
determining the item size.
And I'm going to explain how
that works.
So groups might seem a little
superfluous in this simple list
example.
A group in a compositional
layout typically represents some
repeating structure that you're
going to have, a column of items
or a row of items.
In this case it's a row, but we
really have kind of a trivial
case where we only have one item
per row.
So each item is going to get its
own group.
But groups are always there as a
consistent part of the
compositional layout
description, and we're going to
use them to our benefit to help
define the item sizes.
So Steve explained all about
container-relevant sizing.
Let's look first at our group
size description.
We've asked here for our group,
which is our row in this case,
to span 100% the width of its
container.
The group's container is its
section, which in turn spans the
layout or the CollectionView.
Meanwhile, we've asked for each
group's height to be an absolute
value of 44 points tall.
Now, notice that this has
basically already defined what
our item box should be -- that's
how we want our item sized, the
width of the CollectionView and
44 points high.
So all we need to do up here
where we specify our item size
is to say that we'd like each
item to be 100% the width and
100% the height of its
container, the item's container
being the group.
So that's all it takes to
implement a list.
But the interesting thing as we
go along, we're going to see
that we don't have to make a lot
of code changes to get some
pretty dramatically different
layouts.
Let's take a look at another
example, that being I'm going to
open up our grid here.
And this is a five-column grid
of edge to edge items.
And we can see if I rotate the
phone, that this layout is
indeed described in such a way
that we always get five columns
across.
The items always remain square,
and they're always sized such
that we get exactly five columns
wide.
Now, this is an example where
groups actually come in a bit
more handy and it will be easier
to understand what their
function is.
So opening up the grid view
controller source file, looking
at our same create layout
function, again, we have that
item, group, section layout
structure.
Basically looks like our list
layout description.
The only thing that's different
here is the item and group sizes
that we specified.
So let's look at how those
differ.
We still want each group --
since a group represents a row
-- to span the entire width of
the CollectionView.
That's great.
But now we don't want our items
to be the same width of their
group; we want to have five
items across.
And the way that we achieve that
in this example is up here we
specify the item's width to be
20% of the width of itself
container.
The item's container is its
group or row, so we're going to
have -- we're going to be able
to fit exactly five items across
because of this width that we've
specified.
Now, as for the heights, instead
of an absolute point value,
we've declared that we want the
height of each group or row to
be 20% of the width of the
group's containers.
And notice we're using this
ability to specify fractional
widths or heights across axes
here.
And this is really handy because
it's how we make our items
square.
The item width and the group
height are the same.
And since the group's height
sets the item's height, all we
need to do is set our item
height to be 100% of the group's
height.
So that's it, that's pretty
simple for creating a grid.
And we did so without really
fundamentally different code
than we used for implementing a
list.
Now, often if you get a layout
from your designers, usually
you'll want to have some space
between items.
So let's look at how to add that
next.
Bringing up the inset items grid
example, we still have a
five-column grid, but we just
have some space now around and
between the items.
Let's open up our inset items
view,
InsetItemsGridViewController to
take a look at that.
And here we have, if you compare
this with the previous example,
you'll see that there's only one
line of code different here.
And this is a useful observation
because you can really think
about this layout as being
computed almost exactly like the
previous edge-to-edge layout.
Each item is allocated the same
box that it was given before,
the same edge-to-edge squares.
But in this case we've decided
that instead of sizing our items
to take up the full square
they're allocated, as a last
step we're going to inset the
item's content by five points on
each side.
So item content insets work
usefully in this way.
Sort of a last-stage subtraction
from the layout that was already
computed.
So that's really cool.
We've seen how to do lists and
grids.
And we've achieved our grids
using relative item sizing.
But there's another really
powerful way to be able to
grid-like layouts with rows and
columns.
And I want to show you that so
we can be familiar with it.
So I'm going to pull up our
two-column grid example here.
And superficially this looks
pretty much just like the
five-column grid, we just have
fewer columns, right?
And indeed, if I rotate the
device, it adapts: It's always
two columns and the items expand
to be the appropriate width.
But this is implemented a bit
differently and it's instructive
to take a look at how.
So in our
TwoColumnViewController here,
we're going to look at our
createLayout function.
And so the interesting things to
notice here, again, we have that
item, group, section layout
structure.
But the first thing to pay
attention to that might not be
immediately obvious is that we
are constructing our horizontal
group that represents each row
in a slightly different way.
We're using a different form of
the initializer that takes an
explicit count parameter.
Here we're explicitly specifying
that we want to have exactly two
items per group, two items per
row.
Now, this will cause
compositional layout to
automatically figure out what
the item width has to be in
order to make that happen.
We do specify an item width here
because we always have to.
We say 100% of the container.
But that value at the top ends
up effectively getting
overridden.
When you ask for a certain
number of items per group,
compositional layout -- that
compositional layout is going to
take that as kind of an override
and it's going to compute
whatever width is actually
necessary to fulfill our
request.
We're also using a different way
of putting space around and
between the items.
Compositional layout offers a
variety of ways to do this,
which makes it a really flexible
API.
Instead of specifying itemInsets
in this case, we're specifying
contentInsets on the section.
So here we want to have a little
margin on the left and right
side.
And we only have one section, so
this basically applies to our
whole layout.
So we're asking for 10 points of
leading and 10 points of
trailing space.
Notice while we're on this line
of code that compositional
layout is structured to
encourage you to express your
layouts in layout
direction-agnostic ways.
So instead of left and right
explicitly, we're specifying
leading and trailing.
And this is fantastic because
when you run your app in a right
to left language, you're going
to automatically get the right
layout adaptation.
Here we're also using a property
called interItemSpacing on the
group.
So we can ask for a group to put
a certain amount of space
between its items, in this case
fixed spacing of 10 points.
And that's it.
Everything else is very similar
to the previous examples.
We're just using that ability to
explicitly specify the number of
items per group.
So that's all pretty neat.
But Steve mentioned another
really cool capability that I
want to dive into.
He mentioned that it's possible
to have a different layout for
each section.
So far we've only looked at one
section layout so far, but what
if we want to have multiple
sections, yet have them able to
each have their own distinct
layout?
So let's open up our distinct
sections example.
And here we have a layout that's
sort of a composite of some
others that we've done.
So we have three sections in
this layout.
The first section is a simple,
single-column list like we had
before.
The second is a five-column grid
of square items.
And the third is a three-column
grid of rectangular items.
So how do we implement this?
Let's open up our
DistinctSectionsViewController
and look at our createLayout
function.
Now, at first glance this looks
somewhat significantly
different, but it's really just
that the outermost component of
it where we instantiate the
layout, instead of coming at the
end, it's coming at the
beginning.
And here's the reason why.
So we're instantiating the
compositional layout using an
initializer that takes that
section provider closure as its
parameter that Steve mentioned.
So section provider closure, I
mean, this is an arbitrary block
of code.
So as you can imagine, you can
respond to this being invoked by
returning whatever kind of
layout you want for that
section.
Your past two parameters,
section index that tells you
which section -- it's going to
be zero, 1 or 2 in our case.
And a layout environment that
contains various useful
properties you can refer to.
We're going to look at that in a
moment.
So -- but everything else inside
this closure is just the same
code that we wrote before.
We're specifying an item
description.
We're wrapping that in a group
description.
And then lastly, we specify a
section description, and that's
what we returned from the
section provider closure.
Now, compositional layout knows
to automatically invoke this
closure whenever it needs to
requery for a new description of
a particular section.
So the contents are pretty much
very similar to before.
The interesting thing that
differentiates our various
layouts is mainly that we have a
different number of columns,
right?
So we've declared this
SectionLayoutKind type, it's
declared at the top of the
source file.
Let's take a look at.
When we initialize it, we pass
in the section index that's
going to be zero, 1, or 2.
And all it does is map that to
an enum type that tells us the
SectionLayoutKind is going to be
either a list, or grid5, or
grid3.
And we've also added this handy
gettable property, columnCount,
where we can just ask that
SectionLayoutKind value how many
columns we should have in that
layout.
Going back to our createLayout
function we can see where we ask
for that columnCount, and we use
it in two different places.
As in our two-column grid
example, we're explicitly
passing the number of columns
that we want when we instantiate
our horizontal group.
So we're leaving it to
compositional layout to
automatically figure out what
sort of item width that implies.
We're also using the number of
columns here to determine the
group height or the row height
for the layout since we want to
vary it.
So you can do this any way that
you want.
So that's pretty neat.
But what if we want to be able
to have this layout adapt,
right?
What if we rotate the phone and
maybe this isn't making the best
use of space?
Maybe we could fit more items.
So let's look at our adaptive
sections layout.
Now, at first glance this looks
exactly like the previous
example.
But when we rotate this one, we
can see that our first section
adapts to show two columns
across; our second shows ten
across; and our third shows six
across.
So how do we implement this in
code?
It's actually quite similar to
our previous example.
Opening up our
AdaptiveSectionsViewController
and looking at the createLayout
function, this is very similar
to the previous example.
The first thing that we'll
notice is different that we've
changed our SectionLayoutKind
type.
So we still have the
SectionLayoutKind.
But columnCount is no longer
just a gettable property, it's a
function that takes a parameter.
And we're passing it a width
that we're getting from that
layoutEnvironment.
So that layoutEnvironment type
contains information such as the
overall container width that
your layout has to work with,
and on iOS it also has trait
collections info inside it.
So you can use all of that
information to figure out what
kind of layout is appropriate
for the current environment.
Here we're just using the width.
And if we look at the top where
we declared SectionLayoutKind,
we can see that columnCount
function that's now a function,
and it takes a width parameter
that we passed in.
That's the width of our
CollectionView basically in this
case.
And we've basically implemented
a layout break.
We decided that if we have more
than 800 points of width to work
with on our device, we're going
to go into what we call
wideMode.
And in wideMode we simply return
a larger number of columns for
each section.
And that's it.
If we go back to our create
layout function, we can see that
after we've got that number of
columns, we pretty much use it
in very much the same way as
before.
So it's very little code change
to get all the way to having an
adaptive rotating layout.
So this is all very neat.
But so far we've only really
talked about items.
We haven't even delved into what
you can do with supplementary
views and decoration views.
And one of the really cool
things I love about
compositional layout is that it
makes it easier than ever to be
able to go beyond just headers
and footers and work with
arbitrary supplementary views of
your own design.
So to tell us a bit more about
that and to take us into some of
the more advanced topics about
other cool stuff you can do with
compositional layout, I'd like
to invite my colleague Steve
back on stage.
Thank you.
[ Applause ]
>> Steve Breen: So now that
we've seen how the basics work,
how you get up and running with
compositional layout, I'm going
to walk through how we deal with
more advanced topics.
Because there's a ton of
different ways you can do custom
layouts with compositional
layout.
All right.
So first I want to talk a bit
about supplementary items.
So CollectionView manages three
basic view class types: Your
cells, the things you interact
with to express your model
objects; it also represents
supplementary items and
decoration items.
Now, these are meant to adorn
other parts of your layout to
give you visual cues about
content information, like maybe
a badge on a cell that says,
"Hey, you got a comment on your
tweet," or whatever.
And we see common uses for these
today with these three examples
-- badges, and headers, and
footers.
And we have support and flow
[inaudible] today with sticky
headers and footers.
And they float above that
content.
But we extend this in
compositional layout, making
this a whole lot easier.
And we can simplify this with
this notion of being able to
anchor content onto an item or
group in your layout.
It simplifies that visual
relationship, how that might
work.
All right, let's look at this
visually.
So here we can see we've got
this new type,
NSCollectionLayoutAnchor.
And here we specify the
relationship between these two
types.
So our supplementary is going to
be anchored relative to the
geometry of a host space, an
item or group in these kinds of
ways.
And it's super easy to reason
about.
Okay. So here we see we have
NSCollectionLayoutAnchor we
create right away.
And we specify the edges.
We want this item to be pinned
to the top trailing side of that
particular cell.
And we want it to poke outside
of that geometry a little bit.
And we see that with fractional
height.
Okay. So here we can see the
device.
So we've got these badges on
there, right?
With my wonderful design skills.
We've got a four-item grid with
those wonderful corn flower blue
cells.
And we see here that some of
these items have these little
notifiers on them, right,
letting them know, "Hey, you got
something to pay attention to
here."
And with this, they kind of poke
outside that geometry a little
bit, right?
They're not really inside the
geometry of the cell itself.
Let's go back to the slides real
quick and walk through this.
All right.
So that fractionalOffset is what
buys us the ability to poke
outside a little bit.
We're going to move over
fractionally 30% in the positive
X and then up in the minus Y 30%
as well.
And then we see we defined the
CollectionLayout
SupplementaryItem with a
badgeSize and elementKind.
So we're going to refer back to
the view class for
CollectionView with that
registered supplementary type.
And then we specify the
container's anchor, specifying
how it's going to relate.
So now that we have this
definition of our supplementary,
we need to associate it with
something.
And that needs to be associated
with an item, a cell.
So in this case we're going to
initialize it with an extended
variant of initializer that
takes an array of
supplementaries.
And that's it.
All right.
So what about headers and
footers?
So headers and footers are just
a tad bit different than
supplementaries for these items.
When you think about the content
that you want to adorn with a
supplementary header and footer,
you really don't want that
supplementary heard and footer
to occlude the content; you want
to extend that content area so
you can see the content itself.
So in this instance we have a
different variation of
supplementary called boundary
supplementaries.
We're going to bin it to the
boundaries of that supplementary
-- of that host geometry.
Now, we can do these boundary
supplementary items for sections
or the entire layout, and we can
pin them to this whole balance,
which do some pretty cool stuff.
All right.
So I'm going to switch back over
to the device here hopefully.
There we go.
All right.
Let's see what this looks like.
So pretty straightforward
example.
Here we have a pinned section
headers and footers.
And we scroll and everything
looks just like we expect.
Look at the code.
All right.
So here's what we got.
So it's similar to what we say
just a minute ago with the
BoundarySupplementaryItems,
except now rather than that
container anchor, we see we have
alignment property.
And here we specify top for the
header and bottom for the
footer.
We want to go to the top and the
bottom of that section of
geometry.
And to make sure that header
kind of floats and stays pinned
to the content area where it's
at in the section, we just
specify PinToVisibleBounds.
And then we need to associate
the header and footer with the
geometry it's going to be in,
and that's the section.
We just do that with the
BoundarySupplementaryItems
array.
Pretty straightforward.
All right.
So by now you've played around
with the brand new iOS 13 card
presentation, this whole card
design language all throughout
the system.
And we see this in scrolling
UI's as well where all kinds of
content is grouped together
logically with cards.
And this is a natural fit for
CollectionView because we always
had support for notions of
decoration views.
Well, in the past you had to do
the math yourself.
Well, now we've made it a lot
simpler with compositional
layout.
And we support this with a
CollectionLayoutDecorationItem.
You just create it with an
element kind and you're done.
This is intended to be used to
have a view that's behind the
section content itself to give
you that nice visual grouping.
And to construct it, there's
just one line of code.
And then to add it to the
section, you just want to
specify the items and off you
go.
Here we go.
All right.
So let's take a quick peek and
see what this looks like.
All right.
Pretty straightforward.
So there's our list with
multiple sections, and we have
our decoration views with just a
single line of code.
So pretty simple.
All right.
So here's a topic near and dear
to my heart: Estimated
cell-sizing.
So in iOS 13 we spent a lot of
time making this fast and
accurate.
And compositional layout extends
the notion of estimated
cell-sizing in very specific
ways.
It allows you to do per-axis
cell-sizing.
And this is super important.
Because oftentimes the view
hierarchy that you want to get
the content of when you render
the content, you don't want to
necessarily be fully constrained
and wild on all the axes and
grow on the X and grow on the Y.
You might know how wide the
thing is, for example.
An example of our headers and
footers, we might know that we
want it to be exactly the width
of the CollectionView.
But, you know, we want the
height to vary a little bit.
So I'm going to show an example
of this real quick.
And we've already been in this
code before.
So here we see our very familiar
sections, headers and footers.
I'm going to pull down here and
go to my text size widget and
ramp it up.
All right.
So here everything's just
adapted.
It's done the right thing,
right?
Now, how do we do this?
All right.
So here we can see that the
height dimension's estimated.
That's all we did differently
here, right?
We specified that we knew
exactly how wide this thing's
going to be, but we don't know
about the height.
We just estimate it to be 44
points.
So as the content's rendered, we
have a better idea.
We can invalidate the layout
automatically.
And this happens all
automatically, makes it really
easy to support dynamic type in
your applications, even with
supplementaries and headers and
footers.
So this is -- this is pretty
great.
All right.
So now we're getting to some
really fun stuff.
So what about nesting things?
So we talked about this notion
of composition early on.
Well, let's talk about how that
works.
So the core layout thing inside
of compositional layout is
layout group.
And layout group is actually a
subtype of
NSCollectionLayoutItem.
Now, because this is a
relationship, when you specify
the items that are in a layout
group, you can also have other
groups.
So you can nest these.
And there's no limit to this
particular nesting, it's
arbitrary.
And because we have this, it
unlocks a ton of interesting new
designs.
Okay. So in this example we see
a group is compromised of three
items.
And we see this big old monster
on the leading side and it has a
vertical group on the trailing
side, right?
So how would we reason about
this in code?
All right, so this is pretty
straightforward, right?
We had this horizontal group on
the bottom here.
And its subitems are the
leadingItem itself, right, and a
trailingGroup.
So it's very easy to reason
about these kinds of things.
We can see right away what we're
trying to do.
And we just compose in
additional stuff and we get
these great layouts.
All right.
So nested groups are pretty
cool.
But what about -- what about
nesting CollectionViews?
All right.
So here we see the App Store,
the refresh for iOS 13.
And I don't know about you, but
when I was a third-party
developer, if I saw this
particular design handed to me,
I'd have a heart attack.
This is complicated stuff.
This can be challenging.
There's a lot of bookkeeping
involved.
And -- but it's a common
pattern.
We see it all the time in
today's applications.
And to make this perform well
and look great is a bit of a
challenge.
Well, compositional layout
solves this with one line of
code.
And I want to show you a demo of
this right now.
[ Applause ]
Okay. So here we have the same
group I had from the prior one.
But we notice the group's a
little bit squishier, right?
It's about 80% of the
container's width.
And this is a vertically
scrolling CollectionView.
It's got five sections on it.
But each section scrolls
orthogonally with that single
line of code.
[ Applause ]
All right.
And we've got a ton of different
variations of this.
And bear with me.
I'm going to switch around here.
All right.
So we've got five different ways
to do this, right, including the
none case, which I don't want
that.
We have two continuous cases.
And then we have three paging
cases.
All right.
So I'm going to walk through
each one of these in kind here.
All right.
Go to our orthogonal scrolling
section behaviors demo.
And continuous is just like
you'd expect it.
This is a very simple,
straightforward scroll view-like
behavior, you know?
You -- you had me at bounce,
right?
It's got that great -- gosh, I
could do this for hours, right?
All right.
So that's just the normal scroll
view behavior.
But we have an additional one
for this continuous, you know,
fluid kind of scrolling that we
call continuous group-leading
boundary.
And that's a mouthful but it's
pretty descriptive.
So as we scroll and we impart
volume to it, we naturally come
to rest at that group's
semantic-leading boundary.
[ Applause ]
So you people that clap have
done targeted content offset or
proposed offset before and you
know it's a pain.
All right.
So with the addition of the
continuous behaviors, we have
some paging behaviors.
And this gets really cool.
So this is just like the normal
scroll view paging behavior, and
we named this behavior paging.
Pretty creative.
And here we can see no matter
how much velocity the user
imparts, we just get one page of
content.
And this page is defined as the
default scroll view behavior
where it's the width of the
CollectionView.
We have two more variants to
this, okay?
And as you can probably intuit,
we have this notion of group
paging.
So now we have the semantic
notion of what a group is, we
can make the page size be the
size of the group.
This gives you a really nice
behavior.
Yeah.
[ Applause ]
Where you can automatically get
that no occlusion thing going
on, right, where your content's
always front and centered.
And then the final one is group
paging centered.
It's just like group paging, but
now we've centered that group
for you automatically.
[ Applause ]
Yeah, it's pretty cool, right?
And that gives you that great
peekaboo effect, right, where
you can see the content leading
and trailing on the side, you
know exactly what's going on.
All right, so that's a kind of
an advanced tour of some of the
additional features in
compositional app.
There's a lot more.
Grab the SDK and check it out.
So at Apple, the notion of
dealing with collaboration is
super duper important to us.
We have to work with other teams
all over the company to solve
problems.
And as a framework engineer,
it's super duper important for
us to deal all the different
teams in the company to make
sure we know what their needs
are for new framework features.
And one of these teams, the App
Store team had a new redesign
coming up for iOS 13.
They really wanted to simplify
their code base quite a bit.
So as part of that conversation,
we talked about compositional
layout.
And they were real excited about
it, went through a bunch of
code.
So for this, I'm going to bring
up one of our adopters from the
App Store team, Dersu Abolfathi.
Dersu?
[ Applause ]
>> Dersu Abolfathi: Thank you,
Steve.
The App Store is a destination
for millions of customers
looking for apps that help them
get the most out of their
devices.
Many of you will visit the App
Store on a daily basis, so the
content needs to be rich,
engaging, and dynamic.
CollectionView plays a key role
in delivering that experience.
So here's the App Store.
If you wanted to build UI like
this today using just flow
layout, you'd probably start
with a CollectionView that
scrolls in the vertical
direction.
Then for every single section
that scrolls on the horizontal
axis, you'd need an additional
CollectionView, which means more
support code to intermediate the
presentation and behavior of
each collection.
With compositional layout, this
can be done using just a single
CollectionView.
In fact, we've done just that.
In iOS 13, App Store has been
rearchitected using
compositional layout.
Each of the content types you
see on this page are capable of
vending their own layout
description.
And all those layout sections
come together to comprise our
one overall
CollectionViewLayout.
We describe this section using
just a single layout item which
has a known height and which
occupies 100% of its container's
width.
That layout item sits inside of
a layout group, which itself
occupies half of its container's
width.
And these are really all the
basic building blocks we need to
get this UI working.
We take that layout group, stick
it in a layout section, and to
get that paging behavior we
want, we set our orthogonal
scroll behavior to group paging.
And we're off to the races.
In iOS 13 App Store is also
getting UI support for languages
that read from right to left.
And compositional layout helps
make that possible.
We construct our layouts using
all the same primitives that
you've seen here today, and
compositional layout just takes
care of the rest.
It ensures that the placement of
our supplementaries and our
cells is appropriate for our
right to left environment.
And furthermore, we don't have
to write a single line of code
to make sure that our paging
behavior also translates for the
right to left layout direction.
So this new API has enabled to
us take all of those scrollable
regions that we were previously
micromanaging and flatten them
into a single CollectionView at
the top level.
All the while, our code has
become more concise, easy to
reason about, as well as easier
to maintain going forward.
Compositional layout has
reimagined the way that we think
about CollectionViews in our own
application, and we can't wait
to see how it enhances the
experience for the apps that all
of you bring to the App Store.
Back to you, Troy.
[ Applause ]
>> Troy Stephens: Thank you so
much, Dersu.
Boy, that is a beautiful
redesign.
And we couldn't be more thrilled
to see compositional layout
already making a difference
simplifying the development
process for one of our prominent
user-facing apps.
And I'm so happy to be making
the same API available to
developers across our platforms.
Speaking of which, I'd like to
show you a real quick demo of
compositional layout working
with NSCollectionView on Mac OS.
So when you open the sample
project that you downloaded in
Xcode, notice that there's a
build scheme and target for the
Mac.
And we're going to build and run
that here.
And let's just open up all of
our layout examples that we
looked at in this talk, and
we'll fan them out.
So here we go.
Here they are, our various
compositional layout examples.
And these are essentially just
the same code that we use.
We're programming to the same
API's.
The only adjustments you're
really going to see are for
metrics where we wanted to apply
more macOS-appropriate metrics
for things.
But we have orthogonal scrolling
working here.
And, of course, on the Mac we
expect things to behave in
Mac-like ways.
And one of the things that can
happen with CollectionViews on
the Mac is they're continually
resizable usually, right?
So we've got -- we've made sure
that resize is nice and fast,
super light-weight, these
layouts reevaluate very quickly.
And our adaptive sections layout
has a layout break just like
before.
And now that you know how this
works, you can easily add
additional layout breaks for
wider window widths and screen
widths if you want.
Of course, we have features like
being able to click items to
select them.
I can use the arrow keys and
navigate around.
I can hold down the shift key
while arrowing around to
accumulate a selection.
[ Applause ]
And, of course, I can drag to
rubber band select.
I can bulk select items.
And this works in any old
compositional layout, including
more advanced ones like this.
[ Applause ]
So all the stuff that users
would expect on the Mac.
And we've got all these layouts
running.
And enough of the code down
below, the item section and
group descriptions is in common
in terms of the API that's used.
You could even factor out code
if you want and share code
between your different platform
projects if you want to.
So we can see that compositional
layout is really here to make
our lives a lot easier across
our various platforms.
And it really has a lot to
recommend it.
It's available for you to start
using today on iOS, tvOS, and
macOS.
And it makes it incredibly easy
to create new custom layouts for
CollectionView to use by simply
describing it.
We think this is a big
game-changer.
This in turn makes
CollectionView an even more
versatile tool than ever before
for presenting your content in
arbitrary ways however you want
to.
And the ease with which you can
describe new layouts, and adjust
layouts, and make different
changes, and try different
things, and iterate on them is
going to enable much faster
iteration with your designers.
So we think this is a really big
game-changer.
So go out, take the sample
project, experiment with it, try
changing various things, use our
code as a basis for yours and
start working on the custom
layouts for your next app
versions.
And we can't wait to see the
delightful user experiences that
this is going to empower you to
create in a fraction of the
time.
Now, if you found this talk
exciting, we've got another that
I think you're really going to
love, especially if you work
with collection views or even UI
table views.
In Advances in UI Data Sources
we introduce an entirely new
radically simpler API for
feeding model data to your
CollectionViews and your UI
table views while getting
automatic computation of
differences and automatic
animations for free.
It's going to create delightful
users experiences.
So make sure to check this
session out.
Thank you so much for watching.
[ Applause ]