Transcript
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Silence ]
>> Thanks so much for
coming to What's New
in Table and Collection Views.
This is a topic of special
dearness to my heart
as we give it every year.
Well, not exactly at this
session but something
on collection and table views
we usually to get to talk about.
And that's really
because table views
and collection views
are a tool that get used
in nearly every single
application and we recognized
that as we went through to add
a lot of improvements to iOS 8.
We really wanted to do things
that made your life
easier doing awesome things
with the tools that, as I said,
just about everybody uses.
So, what we're going to talk
about today is a few
fundamental improvements
to both table view
and collection view.
And we'll be beginning
with table view.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And we'll be beginning
with table view.
In particular, the big thing
that we're going to be talking
about today is how to
do Dynamic Type adoption
in your table views.
We're going to be asking that
all applications that deal
with text in iOS 8 adopt Dynamic
Type and you'll see in fact
that all of our system apps
have already done this.
So users are going to expect
that your application do this.
And I'm going to show you that
it's really quite simple to do
and I think you'll have
a lot of fun with it.
The mechanism that
we're giving you
for adopting Dynamic Type
is self-sizing cells.
We think this strategy--
thank you.
[ Applause ]
This strategy is really
going to improve the way
that we architect our
table views, number 1,
because you can encapsulate
logic right in the cells, and 2,
the fact that you can do that
will make it so much easier
to have cells that are not a
hard coded compile time known
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to have cells that are not a
hard coded compile time known
height which means you can
easily adopt the dynamic
font changes.
So, additionally, we've got some
new things in collection view.
And number one, really great is
we're bringing self-sizing cells
to collection view also.
[ Applause ]
And for all of you custom layout
authors, I know there's a lot
of you out there because every
week at least I see a new app
that has a really great layout
that really impresses me.
We have something
we want to talk
about which is smart
invalidation
which will allow you
to write great layouts
that are also super performant.
[ Applause ]
So, we begin by talking
about table view
and Dynamic Type adoption.
If you've never seen
this screen from iOS 7,
is when we introduced
Dynamic Type, this is a screen
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
is when we introduced
Dynamic Type, this is a screen
in Settings where you
can change the text size
to fit what feels
appropriate for you.
And if you're somebody who
likes text a little bit bigger
so that you can see things
easier, then you can slide
that over and magically the
text changes in the interface
and in fact in iOS 8, all
internal apps will adapt
and change their text size based
on this slider value
that you've set.
So that's great because it
makes your phone truly feel
like your phone, and then
adopts to your needs.
And since all of the internal
apps are doing this adaption,
we will expect third party
apps to do that as well.
So, let's talk briefly about
what it takes to get there.
Well, we can do it by
using built-in labels.
Built-in labels in table
view cells have dynamic fonts
on them automatically in iOS 8.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, if you're using the text
label, the detail text label,
they have already dynamic fonts
and as the user changes
the text size,
those labels are
going to change size.
If you have your own custom
labels, it's really as simple
as adding a preferred
font to that label.
This is another API that existed
in iOS 7 so it's not new.
We're just expecting
everyone to use it now.
So, simply changing
your font from something
that may have been, you
know, setting a font
of a particular size to
instead using a preferred font,
you now have Dynamic
Type in your label.
But that brings in
a wrinkle to things
and that wrinkle is now things
are dynamic and it's dynamic
because of the size of the
text that can change based
on what the user has set.
So, if we think of simple
table views like Settings
or like Messages-- I guess
that Notes actually--
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
or like Messages-- I guess
that Notes actually--
Messages and Mail or Contacts,
these are all simple table views
that in iOS 7, the row height
was known at build time.
You could say, hey,
that's a 44-point high row
and that's always true.
But on iOS 8, that's not true.
A user changes their text size,
all of those row heights change.
So, even the simplest of table
views from iOS 7 now need
to think about being
dynamic in iOS 8.
So we have a few strategies
to achieve dynamism.
One is the simplest case.
If you're like one of those very
simple table views that we saw
on the previous page, then you
can use the already existing
property rowHeight.
And even though you're dynamic,
you can still use that property
because when the
text size changes,
when you get a new height,
you can change the value
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
when you get a new height,
you can change the value
of that property to
something different.
And in fact, this strategy has
an advantage if you truly are,
you know, a known height that
will be the same for each cell
because if you can
set this property
for all same size cells,
then you'll get the best
scrolling performance possible
because no calculations
need to be done
on the fly while scrolling.
The next strategy is something
you're probably all familiar
with for using dynamic-sized
cells
and that's implementing
the delegate method,
tableView:heightForRow
AtIndexPath.
That strategy is still valid and
something you can use in iOS 8
if you'd like to size your cells
one at a time, give a height
for each row, you
can still do that.
Of course, the disadvantage
to this
and why we wanted something
new is that if you implement
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and why we wanted something
new is that if you implement
that delegate method, all of
your row heights are asked
for upfront when
the table is loaded.
Even if you have 20,000
rows, we're going to ask
for the height of all of those
rows when we load the table.
And of course, no cells
have yet been created
so your delegate
needs to understand,
without creating cells, what
height to make those cells based
on your content, and that's been
the challenge up to this point.
So that's why we have
the third strategy
which is self-sizing cells.
[ Applause ]
So, let's talk a little bit
about, you know, why this is
and why, you know,
this is a big deal?
If we take a simple table cell
like that in Mail, there's a lot
of parameters around
sizing the cell.
We, you know, we
look at this and OK,
there's actually a
lot going on here.
There are three different fonts.
And in order to size the cell
correctly, we need to know
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And in order to size the cell
correctly, we need to know
about the size of all of those
fonts and how big they draw.
Additionally, there are
margins between the texts.
We need to know how
big those margins are
and there are several of them.
So we have to know
these fonts and we have
to know these margins.
That's a lot of knowledge
if we were
to do dynamic sizing
in the delegate.
So, in 8, I'll show you how
things work and I'll get
out of the way so you
can really see this.
Imagine that we've got
a few rows on screen,
and then we've got a fourth
row that's not yet on screen.
In iOS 8, if you're using
self-sizing cells, and in fact,
part of this is going to
be using the iOS 7 API
which is giving an
estimated height for a row,
something we introduced
last year.
So that fourth row is going
to be sized using an estimated
height that you provide,
either via the property
estimated height
or there's a delegate
method for that, too.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Now, when you scroll or when
you use the scrolls rather,
at that time, we need to
bring on screen row 4,
which up to this point only
has an estimated height.
So at that time, we
will create the cell.
After creating it, we will ask
the cell how big it should be.
Then, we use that
size if it's different
from the estimated size
to adjust the content
size of the table view.
And only then after we've
adjusted the content size
and taken the size
from the cell,
do we actually put
that cell on screen.
And so, what you
probably all want
to know is what happens
in step 2?
How do we actually
size the cell?
So, we're going to
talk about that now.
And there are two ways that
you can communicate information
about how big your cell is from
the cell back to the table view
so that we can get
you the right thing.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
so that we can get
you the right thing.
And number 1 is you
can use Autolayout.
[ Applause ]
If you're already using
Autolayout, and I know that many
of you are, you may essentially
have to do nothing here.
Your constraints, if they
already determined unambiguously
the size of the cell, then we
will just use those constraints.
In fact, the way that we've
implemented this in iOS 8 is
that table view calls
systemLayoutSizeFittingSize
on your cell.
And systemLayoutSizeFittingSize
is smart enough to understand
if you've implemented
constraints in your cell
and if so, the Autolayout
engine delivers the size.
And if you haven't implemented
Autolayout constraints,
systemLayoutSizeFittingSize
calls sizeThatFits.
And then, you can simply
return a size manually based
on your own logic.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So by adding sizeThatFits,
you don't have
to use Autolayout if
you don't want to.
If you are using Autolayout
as it notes up there,
you want to add your constraints
to the content view of the cell.
You always think about
whatever content you have goes
in the cells content view.
And this plays into
what the API contract is
when we are sizing your cell
because there's sort of a--
there's an in and out situation.
The in to your cell is a width
and that width is the
width of the content view.
What your cell returns is the
height of the content view.
So, when you're implementing
sizeThatFits, for example,
the size that we send you
will have the resolved width,
what the width of the
content view is going to be.
And then, the value that you
return has a resolved height.
And even still with your
constraints, you should think
about how you build your
constraints as assuming
that you've got a width,
the content view has a width
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that you've got a width,
the content view has a width
and that was predetermined
by the table view based
on how wide the table
view is, you know,
whether you have an image
view or accessory views
that will make us
shrink the content view.
And then from that width,
your constraints
define a height based
on the content that you have.
So I'd like to spend the
rest of the time, well,
actually the table view time,
telling you or showing you
in code how to actually do this.
We're going to see that this
is quite simple at its base
but I also kind of want
to run through gotchas
that you might run into,
that other people have run
into in the past so that you
can kind of keep the idea
in your head that, hey, this
is simple, but if I go back
to my hotel or if I
go back to my company
and I start implementing
this and it just doesn't seem
to work, come back
and watch this video
because there's a good chance
I will have covered what is
causing your code to not do
what you think it's going to do.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
causing your code to not do
what you think it's going to do.
So let's start off
by going to Settings
and looking at our Text Size.
Right now, I've got it set
to be the smallest thing.
I'll just put it to regular.
And this application I've got
right now just has a very simple
table view and it
doesn't do a whole lot.
It just has a one-line
text label in cells
that are all predetermined
to be 44 points tall.
So, one thing about this that
is interesting, if I'm on iOS 8,
is you take a look at that
and if I change my text size
and I bring it all the way
up, then run the app again.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and I bring it all the way
up, then run the app again.
You see nothing changes
and that's the problem.
We want something
to change on iOS 8.
We want our app to be adaptive
to what the user wants.
So, since in this case
I'm using a custom label
that I've called
headlineLabel here.
I create it right in
my cells initializer.
The problem is that I have
set the headline labels font
to be a specific size.
And actually what I want is to
use a dynamic if I typed font.
And so I'll just change this to
preferred font for text style,
UIFontTextStyleHeadline
is perfect.
And if I run that now, we'll see
that the text did get
bigger and that's great.
It adapted to what
the user wanted.
But this looks a
little weird now
because the cells are the same
size that they were before
and I want the cells to
grow along with the text
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and I want the cells to
grow along with the text
because if I have giant texts,
small cells, things are weird.
So, what I'm going to do is
use constraints via Autolayout
and I've got a whole
bunch of them here,
I'm going to walk through them.
We could do this in Interface
Builder as well but I thought
if we walk through this in code,
it will cause a little
bit more understanding
about what we're actually doing.
So, I have 4 sets of
constraint logic here.
What I'm doing with this first
one is setting what happens
at the top.
I'm setting some spacing
at the top of my label.
So I'm saying that the first
baseline is equal to the top
of the content view plus a
constant and a multiplier.
And the reason that I, you know,
I've picked these values
somewhat arbitrarily,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
I've picked these values
somewhat arbitrarily,
but the reason that there's a
multiplier is I want the space
between the first baseline and
the top to grow not simply equal
to the growth of the
font but I want it
to grow a little bit more so
that we get even more space
between the top of the text
in the top as the font grows.
So then I do something
very similar,
setting a padding at the bottom.
So that's just setting
in attribute bottom
for my content view to be
based off of the last baseline
of the label, again, with sort
of a constant that I just picked
and a multiplier for the same
reason I have a multiplier
up here again.
These values are
not magic values.
They're largely values that
I picked out of thin air.
So that gives me
padding at the top
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So that gives me
padding at the top
and the bottom of my content.
And as the content grows, then
I will still have that padding
and the cell will
actually grow with it.
The last thing I did here
for height is I wanted
to have a minimum.
The human interface
guideline says,
"We want to have touchable
things like table views be
at least 44 point tall."
So, I've set a constraint
that says,
"I want my content view's height
to be greater than or equal
to 44," simple as that.
So even if the text
size gets very small,
I actually still want
to keep the cell to be
at least 44 points tall to meet
the human interface guideline.
For the horizontal axis,
I've just used a simple
visual format language to say
that I want 15 point margins
around my headlineLabel
and I'm just adding
those to the array.
I had all those constraints
in my content view.
And now that I've done that,
let's run it and
see what happens.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
let's run it and
see what happens.
Well, it actually
looks the same.
And, I have something
really scary here.
I have a bunch of Autolayout
constraints failing messages.
[ Applause ]
Thankfully, there is a solution.
So, I mentioned this
earlier but part of being--
or self-sizing cell
aware and using
that in your application is
that you use row
height estimation rather
than literal row heights
in your table view.
So if we look at my
ViewController, in viewDidLoad,
previously I was setting
the row height to 44.
Well, if we do that and
we have self-sizing cells,
the self-sizing doesn't kick
in because I've said row
height is 44 and that's sort
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in because I've said row
height is 44 and that's sort
of the end-all be-all
for table view.
What I really want
now in iOS 8 is
to set the estimated row height.
So, when you run
into this problem,
try to remember Luke said
use estimated row height,
not row height on iOS 8.
And then when you run this--
oh God, Luke's solution
didn't even work.
Well, so here's one
of the gotcha here
which actually will be
fixed in the next seed,
but I wanted to show you--
[ Applause ]
I wanted to show you so
you don't think I'm a liar
when we go back to your rooms.
So, if your cell or if
your table view is coming
out of a nib or a
storyboard, then it's--
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
out of a nib or a
storyboard, then it's--
row height property will
be set when it comes
out of that storyboard.
We want it to be the default.
This is actually the new
default for rowHeight
in iOS 8 is
UITableViewAutomaticDimension.
That means, hey, I
don't have a row height,
figure it out based
on other information.
And this is already the default
for programmatically created
table views and, as of seed 2,
will be the default for
table views that come
out of storyboards
and nibs as well.
It's interesting actually
to know this in general
that this default has-- this
default value has changed
because it used to be 44 and
now it's automatic dimension.
Because if you were doing
math based on the value
of tableViewrowHeight and just
expecting the default value
to be something that is
reasonable to do math on,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that's not true unless
you have actually set the
rowHeight value.
So, if you run into
weird things like all
of your table cells
suddenly shrunk to no width
or no height rather, look
and see if you're doing math
with the rowHeight property.
So, now that I've done that--
oh, thank God, no more weird,
layout constraint messages.
And actually, my cells
have grown a bit.
They look more natural
for this text size.
So now that that is true,
there's something else
that I can do here, and that is
go back to my cell and I want
to make those labels which
are all clipping off the end
of the screen not
do that anymore.
So, let me take headlineLabel,
numberOfLines,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, let me take headlineLabel,
numberOfLines,
make it 0 so it should grow the
height of the label according
to the content that's in it.
If I run that now, now my cells
all become the right size based
on this content that's in them.
[ Applause ]
And that's it.
I don't have any
delegate methods at all.
I'll prove it to you, nothing.
Except for
cellForRowAtIndexPath, I mean,
got to have that, right?
So that's it.
That's all you need to
know to migrate your table
from an iOS 7 style table
to an iOS 8 style table.
And I encourage you to go do
this right away because you want
to be ready by the
time we shift iOS 8
to have your fully adaptive apps
that respond to Dynamic Type
and are just fabulous for users.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and are just fabulous for users.
So that's table view in
iOS 8 and we're going
to spend the rest
of this talk talking
about collection view features
that I mentioned earlier.
And Olivier Gutknecht will come
up to enlighten us as it were.
[ Applause ]
>> Thank you.
Hi, my name is Olivier Gutknecht
and I love collection views.
So today, we have
two new features
in collection view for you.
So first one, of course,
is self-sizing cells.
We added that to table
view and, of course,
we had to enhance
collection view
to support self-sizing cells.
But collection view is quite a
generic class and a key thing
in collection view is you can
actually build your own layout.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in collection view is you can
actually build your own layout.
And when we implemented
the self-sizing cells,
we added a new infrastructure
to do that.
And what we wanted to
do is to actually open
that for your custom layouts.
So if you have a custom layout,
you can actually implement
the same self-sizing cells
techniques we have in
all different layout.
So, self-sizing cells, we
think it's a major new feature
in collection view because
with self-sizing cells,
you can support easily
Dynamic Type.
We're making that available
in UICollectionViewFlowLayout
which is our default
layout we provide in UIKit.
And it's really for
your own custom layouts.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
And it's really for
your own custom layouts.
But first, I'd like to do a very
quick demo of self-sizing cell
in UICollectionViewFlowLayout.
So, this is a very
simple collection view.
I actually tried to make
that simpler but I couldn't.
So, it's a flow layout and
we just set the item size
of all elements of the
screen to be the same CGSize.
So we don't even
have a delegate.
We just used the
itemSize property
on UICollectionViewFlowLayout
and UICollectionViewCells
here are extremely simple.
It's basically just a label
and everything is
sized the same way.
So, of course, it's not great.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, of course, it's not great.
And when you want to actually
adjust your item size based
on the content of that cell, you
can actually do that right now.
You have to implement a delegate
on the
UICollectionViewFlowLayout
to compute the correct
size for a given item
and then the cell
is going to be sized
with what the layout returns.
But it's a bit inconvenient.
Because basically in your
data source, in your delegate,
you have to compute a size based
on the font you're going
to use in this label.
And then, your cell is
implemented with the label.
So, you're basically duplicating
code and that's not great.
So, what I'm going to
do now is I'm going
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, what I'm going to
do now is I'm going
to use the exact
same collection view
to same flow layout,
to same cell class.
I'm not going to change
to a different layout.
I'm just going to
enable self-sizing cells
on UICollectionViewFlowLayout.
And that's what we have.
It's dynamically sized.
The flow layout still
worked the same usual way
by dynamically flowing
adding cells line by line
and it's a new feature in
UICollectionViewFlowLayout.
The only thing you have to do is
to have your cell class
implemented with Autolayout
or using sizeThatFits.
[ Applause ]
So, how do you size cells
with UICollectionView?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
So, how do you size cells
with UICollectionView?
The usual way, the classic
way to size items on screen
with the collection view is
to actually have the
layout compute items,
size and position on screen.
So in that case-- and that was
what was happening with iOS 6
and iOS 7, the collection view
layout decides everything.
The cell is just-- We are just
enforcing a size for this cell.
So, in iOS 8, we're adding
these self-sizing cells mode
which mean that just
like with table view,
you can actually use constraints
on your cell content view.
Or, if you are using
manual layout, you just have
to override sizeThatFits.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
But when setting cells on
screen with a collection view,
a layout basically computes
a set of attributes.
And in these attributes, we have
position, size, but only things
like alpha are even transformed.
And we are actually opening
that also at the cell level.
It mean that if you override
preferredLayoutAttributes
FittingAttributes in your cell
class, you can actually tweak
or the layout itself compute it.
It's extremely powerful.
So, to summarize,
the first mode,
the classic mode,
the layout, the size.
Computes attributes and the
collection view is going
to create a cell and
enforce that size.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
The second mode is
the layout is going
to compute an approximation,
provide attributes.
The collection view is
going to do the usual thing
if you create cell and
then the cell is going
to adjust if needed, that size.
And the third mode, the layout
again estimate the attributes
but then the cell can
actually tweak any property
on the layout attributes
including transform.
[ Applause ]
So, we are making that
available as a new feature
of UICollectionViewFlowLayout.
How can you use that?
It's actually easy.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Step one, we have
a new property.
It's estimatedItemSize.
Because on collection view
we're really talking about width
and height, it's a CGSize.
But apart from that,
it's basically the same model
Luke described for table views.
How do you enable that?
You just set this property
to a non-zero CGsize.
That was step 1.
Step 2 is there is no step 2.
If you want to use
self-sizing cells
in UICollectionViewFlowLayout,
it's one line.
That was my demo.
I replaced ItemSize with
estimatedItemSize and that's it.
That's pretty cool.
[ Applause ]
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
[ Applause ]
Now, I'd like to talk about
how that works and how
that changed things in
the collection view model.
Self-sizing is really
a collaboration
between three things,
the layout,
the collection view
and the cell.
The first thing a flow
layout is going to do is
to compute a first pass,
the first approximation
of your layout.
And based on this
information, what's going
to happen is the
collection view is going
to the queue, create
these cells.
We're going to self-size these
items again using Autolayout
or sizeThatFits or
preferred layout attributes
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
or sizeThatFits or
preferred layout attributes
for fitting attributes.
And then, we are
actually sending back
that updated information
to the layout.
The layout can now decide
if it wants to react to that
and actually send the final
layout attributes we need
to actually display
cells on screen.
So, a very important thing
about this model is it's
actually the same model we're
using with collection view.
So, layout is in charge
and is always in charge.
But the catch is now we have
a way to actually communicate
to the layout that maybe we
should actually adjust things
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to the layout that maybe we
should actually adjust things
because the cell size
might be slightly different
from this first approximation
we computed before.
That's a very generic and
extremely powerful mechanism.
So, now, I'd like to
talk about custom layouts
and smart invalidation.
But first I'm going
to refresh your memory
about how collection view
layout and validation works.
So, if you have a custom layout,
if you got a custom layout,
you know that the first
time we try to present cells
or supplementary views or
decoration views on screen,
the first method we are coding
on your custom layout
is prepareLayout.
Usually, that's when you can
actually pre-compute things
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Usually, that's when you can
actually pre-compute things
or cache some attributes,
have a generic idea
about what your layout
is going to be.
Because the next
thing we're going
to ask you is the overall
size of your custom layout.
And based on that and
based on what section
of this layout we want
to show on screen,
then to ask the layout to
provide a list of attributes
for items in a given rect.
For instance, usually
the visible bounds
of the collection view.
Based on these attributes,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Based on these attributes,
the next thing is the
collection view is going
to do the usual work of
dequeuing, creating cells,
possibly coding sizeThatFits or
applying Autolayout constraints.
So, in that case,
you would return 6
and 6 plus the several
[inaudible] attributes
describing what is in this rect.
Now, you might actually
later invalidate this layout,
for instance, because you want
to apply some special effect
when you scroll like a
cover flow like effect.
In that case, you have
to call invalidateLayout
which is basically a way to tell
the collection view that OK,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
which is basically a way to tell
the collection view that OK,
we are starting again.
What's going to happen is begin
to call prepareLayout again.
And that full cycle
starts again.
So, obviously, there is
a small problem here.
If each time we call or
you call invalidateLayout,
you have to re-compute
all these attributes.
That's the best way to have
a high performance layout.
So, we actually have
a great solution
for you for this problem.
Be lazy or smart
or lazy and smart
which basically means
re-compute only what you need
and that strategy you can
use right now in iOS 7
with something we call
invalidation contexts,
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with something we call
invalidation contexts,
because that's a class
that we introduce in iOS 7.
And this class is basically
a tool for your custom layout
to indicate what changed
because it's your layout
so you know what to do.
So, invalidation context in
iOS 7 were basically a way
to provide fine-grain
information to your layout
when things are invalidated.
We were actually using this
invalidation context class
already for things like rotation
in UICollectionViewFlowLayout.
And now, we are using that
for self-sizing cells.
So, in iOS 7, when using
an invalidation context,
it's actually easy.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
it's actually easy.
You have to define a collection
view invalidation context
subclass exposed at
your layout level.
And instead of coding
invalidateLayout
which is the very generic,
remove everything
invalidation method,
we can actually call
invalidateLayoutWithContext
and pass an instance
of your class
where you're probably
adding some information
about what you should do in a
given invalidation situation.
We also provided an override
point for bounds change
which is a common
thing you want to do.
So if your layout is
continuously invalidating
on scrolling on bounds
change or rotation,
you can actually
just override that
and return your invalidation
context
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and return your invalidation
context
with information
you can use then
in your layout implementation.
We actually added in iOS 7
in this invalidation context
two additional information.
Sometimes when we invalidate,
it's because the
data source changed.
You have items coming in
on or out or we just have
to invalidate everything
in some situations.
These were two properties.
The collection view was setting
up for you and you could use
in your layout implementation.
So, iOS 7 invalidation
contexts were really a class
for your own use basically.
So what's changing in iOS 8?
In iOS 8, invalidation
contexts are really a way
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
In iOS 8, invalidation
contexts are really a way
to establish a communication
between your layout
and the collection view.
You can tell the
collection view that,
"Here is my invalidation
situation.
Maybe you can actually
help me with that."
And the information you can pass
to the collection view
can be useful for things
like high-performance floating
headers, sticky headers
like in table view
when you scroll
and you have this
section header on top.
Another use case is
self-sizing cells.
Oops. Because when cells can
actually change their size,
it might actually change
the overall collection view
content size.
It might change the offset
and invalidation
context are a great way
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
and invalidation
context are a great way
to tell the collection view
that your layout is adapting
to a self-sizing change.
So, first, fine-grain
invalidation.
We added three new methods on
invalidation context in iOS 8,
invalidateItemsAtIndexPaths,
invalidateSupplementary
ElementsOfKind atIndexPaths
and
invalidateDecorationViewsOfKind
atIndexPaths and you
can access this later.
What's great about that?
First, you can actually call
these methods several times.
You're going to aggregate
that information.
What does that mean
for your layout?
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
Let's take a very
simple grid-like layout
with section headers.
And you want to implement
a sticky header on top.
In iOS 7, you basically had
to invalidate the entire rect.
In iOS 8, the only thing
that is actually moving is
that supplementary view.
So what you really want to use
is on your invalidation context,
tell us that I'm invalidating
that supplementaryView
atIndexPath throughout.
In that case, we know that only
one thing is actually changing.
So, we're going to
ask your layout, OK,
give me an updated version
of that supplementary view.
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
give me an updated version
of that supplementary view.
So we are directly going to
call this fine-grain method
on your layout which is already
here, layoutAttributesFor
SupplementaryViewOfKind
section header atIndexPath.
And one thing we are not going
to do is calling
layoutAttributes
ForElementsInRect.
So instead of asking
you for 14 attributes
in that invalidation situation,
we are just going
to ask you for one.
It's amazing for
high-performance layouts.
[ Applause ]
Here's a situation
where you want
to use an invalidation context
is for self-sizing cells.
If you want to implement
self-sizing cell
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
If you want to implement
self-sizing cell
in your own custom layout, well,
you can actually tell
the collection view
that the content-size
is going to change
because that cell was bigger
than expected so you have
to enlarge the overall
content-size.
And you can do that with
a very simple property
on the validation context which
is a contentSizedAdjustment.
So you can give us the delta,
the change between what
was computed before
and the new size.
But, of course, because a cell
might not be what you expected,
it means that the
offset where you are
in the collection
view can change
and you can tell that too.
You can set to
contentOffsetAdjustment
in the invalidation context
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
in the invalidation context
and then the collection view
is going to do the right thing
and adjust the position so
you don't see and they jump
from one position
to another just
because your layout
updated itself.
The other feature we're
adding to help you
with self-sizing cells in
your custom layout is a way
to invalidate for
specific attributes.
And that's a new method
on UICollectionViewLayout.
If a cell, return a
different size, for instance,
or different layout attributes,
we're going to ask you
if we should invalidate
the layout.
And because we want
fine-grain invalidation,
we have an override
point, InvalidationContext
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
ForPrefferedAttributes based
on the original attributes.
So again, first step,
your layout computes
the first approximation.
We self-size the cells which
might change its attribute
and we pass you these
attributes in your layout.
And again, the layout is
still the final decision-maker
on what should be on screen.
So, we just like to summarize
that technique for
a custom layout.
It's a very simple line
based custom layout,
something like that.
So that's my overall
collection view layout.
I have five cells and
I'm going to compute
that first approximation by
using the usual layoutAttributes
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that first approximation by
using the usual layoutAttributes
ForElementsInRect.
So I'm going to assume that
all my cells are exactly the
same size.
But then, collection
view is going
to create these cells
and self-size.
In the first cell, let's say,
adjust to this size based
on sizeThatFits or
through layout
or preferredLayoutAttributes.
And obviously because
of that size change,
my layout is no longer
what I want.
So I will have to update the
position for these other cells.
So, in my layout
implementation, I can decide
if a cell has different
attributes
from what I computed before
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
that I should actually update
these other cells' attributes.
Of course, there is
one last problem.
My collection view content
size is no longer correct.
So, I want to make
that consistent.
So, in my invalidation context,
I'm going to tell
the collection view
that we should adjust the
collection view content size.
And actually it's not the
only thing I need to do
because my previous
state was three cells
in these visible bounds.
A little bit of the first
cell, the second cell
and part of the third cell.
But the next thing is telling
the collection view that based
on this new cell
information we need
to actually adjust the
offset, so visually we are
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
to actually adjust the
offset, so visually we are
in the exact same state.
[ Applause ]
It's, of course,
completely layout specific.
You can imagine using this
technique for circle layouts,
line layouts, grid
layouts, base board layouts.
It's an amazing way to implement
extremely high performance
layout and add new
features like Dynamic Type.
And to conclude I like
to bring back Luke.
[ Applause ]
>> Thanks so much, Olivier.
Well, you probably
got a theme here
that is we've brought
self-sizing cells to table view,
we've brought self-sizing cells
to collection view in the form
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
we've brought self-sizing cells
to collection view in the form
of flow layout and we've also
brought self-sizing cells
to your custom layouts
if you choose
to adopt that methodology.
So, what I want you to
go home with is remember
that every app should
be adopting Dynamic Type
if you've got text
in your app that is,
which I imagine is
every single one of you.
When you're doing
this, you might think
about using self-sizing cells.
It will probably
help your cause.
And if you're using
collection view, specifically,
if you are the author of a
custom collection view layout,
this I can't stress enough,
use invalidation contexts.
This is like the
key to the world
for writing high performance
layouts in collection view.
In fact, since we're going to
have a lab right after this,
and I know some people
are going to come
with performance questions,
I'm going to tell you
X-TIMESTAMP-MAP=MPEGTS:181083,LOCAL:00:00:00.000
with performance questions,
I'm going to tell you
that my first question
for you will be,
are you using invalidation
context.
And so, if you're not,
learn how to use them.
They're spectacular.
There's another great talk
on collection view later today
particularly for those of you
who are writing custom layouts.
This is Advanced User
Interfaces with Collection Views
and there are some really
great tips in here for how
to accomplish non-standard
things, you know,
things that aren't built into
the flow layout that we shipped
and get truly great interfaces
that use collection view.
For more information,
always feel free
to contact our frameworks
evangelist, Jake Behrens,
and check out the documentation.
Thanks for coming.
[ Applause ]