Transcript
>> Jason Beaver: Welcome to the
Mastering Table Views session.
My name is Jason Beaver.
I am an engineer on the iPhone Frameworks
Team, and I will be joined in a little bit
by Luke who will run us through some demos.
Well, we have an quick introduction.
This is going to be a relatively advanced session.
We're not going to cover any of the sort of basic things
of loading the table view, how to get content into it,
how to customize cells, things like that.
We're going to touch on a couple of
issues that we haven't had a chance
to really focus on over the past couple of years.
And hopefully it will allow you
to sort of add a level of polish
to your application that your users will really enjoy.
So we're going to cover a handful of things today.
We're going the talk about the update mechanism built
into Table View that allows you to change the contents
of the Table View without completely reloading it.
We're going to talk about how to
combine this with an editing transition.
If you're familiar with the context application and you
go into editing mode, you'll know that there's some rows
that are added to allow you to add fields
to the contact, and when you come back out,
we delete those rows so that you don't have to see them.
And notice all those animations are synchronized.
We'll talk about how to do that.
We now have the iPad for you guys to deal with, so
we're going to talk about some differences with regard
to the table view between iPhone and iPad.
And we're going to talk about if you need to do some sort
of background loading in your application, for example,
like the YouTube application loads,
thumbnails in the background,
we'll talk about how you do that with Table View.
We're not going to talk about background loading
itself, but how that relates to the table view.
And finally, we're going the talk about using the new
UIGestureRecognizerTechnology that was introduced in 3.2
with table views and how you can do some neat things
to allow users to interact with your table views.
All right.
So let's jump into updating the contents of your table view.
So first of all, why do we want to do this?
Why not just reload the table view every time?
Well, there's one reason, it's
improves performance, it's expensive,
or somewhat expensive to completely reload a table view.
And these are limited performance devices, so just
replacing the content that's changing will make the app
more performant.
But the bigger reason is that it's easier
for the user to understand what just changed.
There's some sort of context shift in your application, and
an animated interface helps the user track what happened.
We're going to look at a couple of simple
examples here where in the one on the left,
we're just going to reload the table view,
and you'll see the content just changed.
It was rather jarring.
The user has to look at it for a moment and sort of try
to figure out what's old and what's new, and typically,
in your case, it's not going to say "new," so
that'll a little harder for the user to figure out.
But in the example on the right, now we're going to
do this in an animated fashion, and when you walk in,
you can see a set of rows were deleted,
you see which rows were inserted.
It was a lot easier for the user
to understand what just changed.
[PAUSE] Table View provides a mechanism to update
individual rows as well as entire sections.
The methods to do individual rows are insertRowsAtIndexPath
withRowAnimation, and similar deleteRows and reloadRows.
There's a corresponding set of methods to do
the same sort of operations on entire sections.
Those are insertSections withRowAnimation,
deleteSections and reloadSections.
So a high level, the way it needs to work in your
application is, you have a model, it has some set of objects
in it that you want displayed to the table view.
You have a data source, which is there to bind your model to
the table view, and then you have a table view with one cell
for each of the objects in your
model that needs to be displayed.
And the way you handle this update is
you first have to update your model.
So in this case, we're going to delete a row.
So you'll do that, your model will get
updated, so now your model just has 4 rows.
And then subsequently, you need to tell the
table view about the change to your model.
At this point, they're sort of out synch,
and if the table view asks for something
that might not be in your -- not be in your model.
So you tell the table, "Yo, hey, we deleted some row."
The table view will then take care of the deleting
the road and animating all of the other rows in place
to now put the table view in synch with your model.
But sometimes it's not as simple as
just a single row insertion or deletion.
You have a bunch of changes to make.
Well, Table View supports batching these.
It -- all you need to do to batch is to wrap all of
those update methods in calls to begin and end updates.
So let's go back to sort of our high level view of this.
We've got a model with some objects, and
again, the data source and Table View.
And now we're going to, you know, delete an object,
and then we'll maybe insert one at a different index.
And now we need to tell the table
view about all those changes.
So we will message it a table view,
we'll set it to begin updates,
and then we'll tell it about the rows we've deleted,
we'll tell it about the rows we've inserted.
And then we'll call endUpdates.
At that point, the table view will
go to the model and try to figure
out what the new state is and then perform the update.
In this case, we'll see that the row that was in
Index 2, right in the center, will be deleted,
a new one will get inserted at Index
1, and the one that was formerly
at Index 1 will actually animate
to its new spot in the table view.
The table view will take care of that automatically.
So there's a couple of important points.
One is that the order inside that
update block doesn't matter.
The order that you specify the inserts versus
deletes or even the order of the inserts
if you needed a multiple inserts,
you can specify those in any order
within the update block and will treat them all as a group.
We'll go into a little more detail here in a moment.
So, here's a simple example where
we're going to insert a couple of rows,
and although we could have specified
these in a single array,.
for example, we split this out into two arrays.
We're going to call it beginUpdates.
We'll make a couple of calls to insert rows
at index paths, and then we'll endUpdates.
And the important to this is here that I can
reverse the order of these two arguments;
the table view is going to do exactly the same thing.
The second is that the table view state when you're doing
batched updates is not updated until the call to endUpdates.
So when you make that call to insert a row
or delete a row, your model doesn't have
to necessarily have already reflected that.
Basically, the model has to be changed, though,
by the time you make the call to endUpdates.
So, now let's talk a little bit about the
first argument to those various methods,
how we actually specify what gets inserted or deleted.
We'll start with deletes.
Deletes simply specify all of the current rows
in the table view which should be removed.
So here, for example, we have a table view with seven rows.
We want to move to a new state.
If you notice, there's Rows 1, 3 and 5 are missing.
All we need to do is tell the table view "delete Rows 1,
3 and 5" because those are the indexes
in the current state of the table view.
And all of the remaining rows will just
automatically animate to their new spots.
Conversely, inserts specify all of the rows which are new.
So here, we're going to start with our table view in the
same state it was at the end of the previous animation.
We want to move back to sort of the original state.
In this case, Rows 1, 3 and 5 are the new ones.
So all we need to do is tell the
table view "insert 1, 3 and 5."
And, again, we can do that in any order.
We can say "insert 5, insert 1," as long as they're all in
an update block, we'll treat those in exactly the same way.
And just like before, all of the
remaining rows will animate into position.
Reloads are specified just like deletes.
They specify all of the current
rows which need to be reloaded.
Here, we're going to start with our table view
at 7 rows, we want to reload Rows 1, 3 and 5.
We just tell the table view "reload Rows 1, 3 and 5."
And just like before, the remaining rows animate.
On this case, there's not much to animate.
But the more interesting thing is
what happens when you combine them.
This is the area that we've seen a
lot of people get into trouble with,
and they get confused about what
exact indexes they need to pass.
But as long as you follow those
basic rules, it all works out.
So here, we're going to start with our
table view in exactly the same state.
We're going to move to a new state.
And if you notice, there is some
-- a few rows that are missing,
and there's a couple of new rows, there's a reloaded row.
So how do we actually specify what changed here?
Well, the first thing to know that in the new
state of the table view, Rows 1 and 5 are gone.
So we need to tell the table view "delete Rows 1 and 5."
In the new state of the table view, Rows 2 and 3 are new, so
we need to table view, "Hey, you can insert Rows 2 and 3."
And finally, 4 was reloaded and notice that its index
changed in the new state, but we don't care about that.
We specify what load or reload in
the former state of the table view.
And just like before, all the other
rows will take care of animating
where they need to to make room for these new rows.
So let's talk a little bit about
what happens under the covers.
You can understand why sometimes things
fail, and you'll get notifications of that.
The first thing that happens is a sanity check.
When that endUpdates call comes in, or even
if you just do one of the individual insert,
delete or reload by itself outside of an update block,
we're going to go through and just sanity check it.
Well, the first thing we're going to do is
we're going to eliminate redundant updates.
What I mean by redundant updates are if
you've told us to insert an entire section,
and you also tell us to insert a row within that
section, well, that's implicit because that's part
of the operation of inserting the entire section.
It means insert all the rows in that section.
And so, we'll go ahead and just take
out the individual row insertion.
We'll do a similar thing on Deletes
and a similar thing on Reloads.
So what we're sort of left with is just sort
of the essential changes that need to be made.
The next thing we're going to do is call your data
source, and we're going to ask for the number of sections
in the table view and then the number
of rows in each of those sections.
And then finally, we're going to
verify that versus what we computed.
So if the former state of the table
view has 10 rows and you say it --
tell us to insert a couple of rows and maybe delete one row.
When we ask you for the number of rows, you
better tell us "11," right, because it's --
if you tell us any other number, we can't
figure out how to get from the old state
of the table view to the new state of the table view.
The next thing we do is actually
rebuild the table view's geometry.
We do this by recalling the delegate methods
to figure out all of the row heights,
all of the header and footer heights, things like that.
And we'll use that to animate to the new position.
One subtlety here that's kind of interesting is
that you can actually return different heights here,
and we'll take care of animating those new heights.
So if you want to just change your -- the height of
the rows in your table view, it's an easy way to do it.
You can do that in conjunction with a
change of the rows in your table view.
You can also actually just do a simple Empty Update Block.
You can call it beginUpdates immediately followed by
endUpdates with no actual changes to your table view.
We'll go through all the same steps here.
The whole first sanity check bit won't really do
anything because you haven't told us any changes,
but we'll still rebuild and animate the geometry, which
means if you simply want to rechange the row heights
in your table view, you can do
that with an Empty Update Block.
An example where we use this is if you look at the S
and S application and go into or out of editing mode,
you'll notice all of the rows get a little
bit taller as they gain a separator.
When they're in any mode, that's exactly how we do this.
So that's the first argument to all
of those various update methods.
The second argument to all of those
is a row animation argument,
and that let's you specify the animation
style for the row that's getting updated.
So that's done with an [inaudible] UITableViewRowAnimation.
And there's a handful we're going to talk about here.
The first one is the fade animation.
The fade animation simply makes the new
cell that's going to be implemented fade in
and out as the other rows slide in or out of the way.
So we're going to insert a row here right
above where it says "Section 0, Row 3."
And we'll see, if you look at it
carefully, you see that that row faded in,
and now as it's deleted, you'll see it fade out.
We also support sliding the row that's being inserted
or deleted in one of the four cardinal directions.
So for right, for example, in the same spot,
we'll see a row slide in from the right,
and when it's deleted, slide back out to the right.
We support a similar thing for left.
You can see it slide in from the left
and then again back out to the left.
And notice it's also fading as it's doing this.
We also support sliding to the top and bottom.
If you watch it from the top, it'll look like the
row will slide out from underneath the row above it,
and then on the delete, will slide back underneath that row.
And then for Bottom, it looks like the row will just slide
out from underneath the row below it,
and, again, slide back underneath there.
So you have quite a lot of flexibility
to sort of make things move
in the direction that you need in your application.
You can also specify None.
Now, some people have the mistaken
perception that this means there's not going
to be any animations in the entire update block.
Again, this just specifies how the rows that
are being inserted, deleted or reloaded animate.
The remaining rows will always slide to their new position.
So here, we'll see that same row.
Notice it's already fully opaque.
It's fixed in position.
It's not animating.
The remaining rows still animate around it, though.
Finally, in 3.2, we added a new style called Middle.
Middle will attempt to keep the cell that's being
inserted or deleted centered in that gap that's opening
up as we're inserting or deleting a cell.
And in the plain-style table view, it'll also add
some shadows there and sort of give the illusion
that that cell the behind the other rows.
So if we watch that here, you can sort of
see there's some shadows showing and covering
up that cell, and as it deletes, we run those again.
A couple of things to mention, we don't expect that you
should use all of these styles in all circumstances.
For example, in Group Style Table Views, each
section is surrounded by a rounded rectangle.
Right, if you're familiar with settings
application, you know what this looks like.
And we want to give the illusion
that that's an unbroken, you know,
rectangle around all of those cells
as you insert and delete things.
So if you use the left and right animation
styles, obviously, you sort of break that line.
So we don't really anticipate you
to use those, although it will work.
So top and bottom or middle are generally better for that.
The middle in the group style case actually
will not display those shadows, because, again,
it sort of looks funny with that rounded rectangle.
All right.
So that's how to actually to do the updates.
What if we want to combine it with an editing transition?
Well, all we need to do to do that is put the call
to change the editing state inside that update block.
So here, we'll call to beginUpdates.
We'll do some amount of modifications to the
table view, and then we'll simply call setEditing
with whatever the new editing state
is and then the call to endUpdates.
We actually will do the transition
in the call to endUpdates.
It won't actually happen immediately when you make
the call, and that'll be the point that we re-query
for the editing styles for all of
the new rows that have come in.
So now, let's bring Luke up and take a
quick demo of what we've seen so far.
[ Applause ]
>> Luke the Hiesterman: All right.
Thanks, Jason.
I'd like to follow-up on all that information Jason
just gave you by giving you an example of an application
that can be greatly enhanced and user experience made
much smoother by using animated updates to table view.
So the application that I'm going to give you an example
of is very simple RSS Reader using the table view
where each section of the RSS Reader represents an
RSS Feed, and we have them one of them open at a time.
So I'll begin by giving you an overview
of the application architecture.
We, of course, have an application and that
application has an application delegate,
and that application delegate displays
a table view controller.
That table view controller has a data source, of course,
as you're used to, which, for the purposes of this demo,
will be opaque, and we won't really
talk about how that works.
I assume you know how to control a
model and put data into your table view.
So that table view controller naturally
has a table view, and that looks like this.
And that table view has a custom section have a view
that you can see there, and it also has custom cells.
Now, most of this stuff, we're not
really going to talk about today.
What we're going to focus on is the table view controller
and what you can do in that table view controller
to customize your table view's behavior, specifically using
animated updates and making things flow better for the user.
OK. So let's first look at the application actually running.
And this is my RSS reader.
Now, if I tap on this little button here
on the side, I can actually open a section.
One thing you'll notice is that section snapped
to its new open stage, so these cells just appear.
Likewise, if I close it, they just suddenly disappear.
I can open it again, open a new section.
And you see the table view just immediately
snaps to the new state with the new cells.
So what I'd like to do is open these sections and
close them and change sections in an animated fashion
so that the user really sees what's going on, and
everything just looks much more smooth and polished.
So if we go to the code, I have two methods
that are going to be important here.
These handle opening and closing the sections.
They are Section Header, Section
Opened and conversely, Section Closed.
And right now, they're both very simple.
All they do is update my model and then they call
Reload Data, and that pops us to a new state.
But what we want to do is get rid of this Reload Data
because that's what's causing us to pop to the new state
because we're just reloading the entire table view.
Instead, we want to use animated
updates to get to the new state.
So let's just start over, and we'll start seeing
what we have to do to use animated updates.
So the first thing we need to do is figure
out what are the rows that we want to insert,
what colors to insert, index paths to insert?
And so we query and get that, and then we
figure out what are index paths to delete?
OK. So now we know the rows that we're
going to get rid of which are whatever rows
in the currently open section and
what rows we're going to insert.
The next parameter, of course, to the insert
and delete rows methods is an animation type.
So we have an Insert Animation and a Delete Animation.
Now, generally, when you're deleting
rows and inserting rows at the same time,
if you're going to use a cardinal direction
animation, you want to use opposite ones.
If you're inserting from the left, say, you want to delete
to the right; that way you get a flow to the inserts
and deletes and there's a sort of a velocity effect.
So here, I'm using top and bottom.
I'm inserting from the top and likewise,
I want to delete out the bottom.
So everything is going to sort of flow down the
screen, and there'll be a velocity that way.
OK. So I have my IndexPaths that I want to insert, I have
my IndexPaths I want to delete, I have animation styles,
I'm just about ready to do my update block.
Of course, before I do that, I
want to actually update my model.
So I do that.
Of course, do this before the update block or at least
before the call to endUpates, is the important thing.
And so now I do beginUpdates and I'm going
to send in the index paths I want to insert.
I do that with insertRowsAtIndexPaths:withRowAnimation
using the row animation that I determined before.
And likewise, I want to deleteRowsAtIndexPath.
And that's the end of my update block.
OK. So, that's the -- that's all the code
that I need for my section opened method.
I just need to mirror some of this for my section closed
method that will only have the logic for index paths
to delete because this is what happens when I tap
on the section that's already opened,
and all I need to do is close it.
So once again, I will just compute my index paths to delete,
go ahead and update my model, and this time I'm just going
to call self.tableView DeleteRowsAtIndexPaths
without an update block.
I could use an update block, and it would behave exactly
the same, but in this case, I only have one action to do,
so there's no batch operation happening, and
that call will do everything I need right there.
OK. So let's run this, and see what we've got.
When I tap on my button here to open the section
now, notice the rows smoothly animate into place,
and there's not the snapping sudden action
that happened when I used Reload Data.
Likewise, when I close the section,
the rows smoothly animate out of place.
There is one caveat to this current implementation.
You'll notice if I open this other section while
Hot News is open, the animation looks rather odd.
Remember, I said before that using top animation for
insert and bottom animation for delete causes this sort
of downward motion effect in the animation of the cells.
Well, that's fine, and that even
looks OK if I open Hot News right now.
But this doesn't look good in this case when I open
the section that's below the currently opened section
because the section header appears to animate upwards.
In fact, it does animate upwards.
So, sort of the moral of this story is
sometimes we need to put some thought
into exactly what row animation styles
we need, and I'm going to do that here
by actually conditionalizing the
row animation styles I use based
on what section I'm opening relative
to the currently open section.
So I'll just get rid of this.
[PAUSE] And I'm going to use that same paradigm as before,
inserting from the top and deleting from the bottom only
if I'm opening the first section or if the section
that I'm opening is above the currently open section.
But in the other case, when the new
section that's being opened is below,
like we saw before where the animation was weird,
I'm going to flip those animation parameters.
So now, I will insert with bottom
animation and delete with top animation.
This will cause all the rows to appear to animate
upwards, which will flow smoothly with the section header.
So, let's give this one more shot.
OK. That looks good.
Now what happens when I open this section.
The whole thing seems to animate upwards, and everything
looks smooth and the same thing happens going this way.
So now I've successfully used Table View Animations
to add a significant level of polish to my application
with just a little bit of code that
got away from using reload data.
And more than that, my user as significant
contextual information that they didn't used to have.
So, that's using animated updates and UITableView
and I'll hand it the floor back to Jason.
Thank you very much.
[ Applause ]
OK. Thank you again, Jason.
So, as Jason said, there are lots of views in the table
view that are potential fodder for gesture recognizers.
The table view itself is a view, the section headers and
the section footers are all views, the cells are views, and,
of course, the table view has a table view
header and a table view for themselves.
And these are all potential fodder for gestures, so
there's lots of interesting things you can potentially do,
lots of places you can do gesture recognition.
I'd like to take a look at the RSS Reader
app that we developed in the last demo
and look at three ways we can add gestures to views in that
application to enhance the user experience end product.
So the first one we're going to look
at is a pinch gesture recognizer.
That's not actually the first, but that will -- that's
one that will be attached to the table view itself.
We're also going to look at tap gesture
recognizer that will attach to the section headers.
And finally, we'll look at a long press gesture
recognizer that we will put on the cells themselves.
So we've got one that will go on the table view, one that go
on the section headers, and one that will go on the cells.
So all of the views in our app we're
going to be using for gesture recognizers.
OK. So to the code.
The first one we want to implement
is a tap gesture recognizer.
And the reason we're going to do that is if we run
our app again, we can tap on this little button here,
and that opens our sections and that's great.
But if these are misses, this relatively
small button or taps anywhere over here,
the touch is wasted, and it doesn't do anything.
We've got all this wasted space that
we could be using for touch handling.
So I want to add a tap that will be
handled anywhere in the Section Header View
that will do the same thing as the button does now.
So we'll see just how easy it is to do that.
I'm going to go to the code for my
section header and it has an ended method.
And in that ended method, I'm simply going
do set up a UI Tap Gesture Recognizer.
So I do that by [inaudible] and I give it -- the
gesture recognizers use a target action pattern,
so I give it the target of Self and the selectors toggle
open which is actually the same method that I used
as the action for the button that is currently
used in the Table View Section Header,
which means I don't have to rewrite that code at all.
I already have all the code that handles the
opening and closing of the section headers,
and I'll just give it the same action
to the gesture recognizer, and I'm done.
So all I have to do now is attach this gesture
recognizer to the -- that went a little oddly.
Oh, OK. Excuse me.
I inserted that in a bad spot.
OK. So now I just need to add the
gesture recognizer to the view
which is the Section Header View, and I can just release it.
And with those three lines of code,
I can now run my project.
And now I can tap anywhere in the section
header, and it works just as before.
That easy.
[ Applause ]
Thank you.
OK. So the second one I want to look at is a
pinch gesture recognizer on the table view.
And why do I want to do this?
I want to do this because when you look at this application,
it's very easy to see that there's a lot more text
that can be displayed in these
cells than fits at the current size.
I could just blow up all the cell
heights and have huge rows in my table.
I don't really want to do that.
What I want to do is let the user decide
if they want to make a cell bigger.
And I'm going to have them do that by actually pinching
the table view and resizing rows in the table view.
So, let's see how we can do that.
Let me go to the interface of my table view,
and I'm actually going to add a couple
of ivars so that we'll use for pinching.
And those are pinched index path and initial index path.
So when I -- when the user starts a
pinch, I'm going to store that index path.
That's going to be the pinched index path so
I know which row they're actually resizing.
And I'm also going to store the initial
height of that row when they start pinching,
and I'm going to use that to compute how
big I should make the cell as they pinch.
OK. So I got my ivars in place.
I'm going to go to the init method
from my table view controller.
I'm going to set up a pinch gesture recognizer just like
I set up the tap gesture recognizer in the section header.
So once again, this is an [inaudible] and I end it with
target of self, which is the table view controller.
And this time I give it a handle pinch as the
action, which I obviously didn't have any resizing
of rows functionality before, so this is a new
method that I'm going to have to write in a second.
But then just as before, I add the
gesture to the view, which, in this case,
is the table view, and then I can release it.
And with those three lines, I've added a
pinch gesture recognizer to the table view.
But I need to go implement handle
pinch so that pinching does something.
All right.
Let's do that.
So hopefully you went to the gesture
recognizer sessions yesterday.
But if not, as a quick introduction, gesture recognizers
fire their actions and they can have one of several states
from possible, began, changed, cancelled and ended.
And we're going to deal with a few of
those in our action method, Handle Pinch.
So this is the outline of the method, and we're
going to do something for when the gesture begins
and UIGestureRecognizer state began, and
we're going to handle the changed state
which we get constant change updates as the
user continues to pinch inward or outward.
And we'll also clean out any cancelled or ended state.
So let's start with the began state.
First thing we want to know is where the user's pinching.
We can get this from a property in
UIGestureRecognizer that is location end view.
So we get that from the gesture
recognizer, and we can use that point
to ask the table view what self,
what index path is at this point?
So we call self.TableViewIndexPathForRow and give it the
pinch location that we got from the gesture recognizer.
All right.
So now we can set our initial pinch height, which we do by
querying our model for what is the height of this row now?
We have the row.
We ask our model what is the height of it?
And we'll just save that off as our
initial pinch height so we can use it
for calculating changes in the pinch height later.
The last thing we need to do is call
a method that we'll write in a second,
which is called Update Pinch For
Scale and send it in index path.
The first parameter is scale we
get from the gesture recognizer,
and that represents how much the user has pinched where
1.0 is the value when the user puts their two fingers
on the screen, and it gets bigger as they
pinch outward and smaller as they pinch inward.
So we'll write that method in a second.
We deal with the changed state by simply doing the
same thing we did at the end of the began state
and calling our update method, Update Pinch For Scale.
And when the gesture cancels or ends, we just have the clean
up after ourselves by releasing our pinched index path.
So, all the work happens in Update Pinch For Scale.
And let's write that now.
So first thing we do is simply
validate the index path that was sent.
And then after that, we need to do some math.
And we'll do that using our initial
pinch height value and the value
of the scale that we get from the gesture recognizer.
So we take the initial pinch height and we multiply
it by scale, and that's how we get our new height,
and we also pin it to a default minimum height, which we
call Default Row Height, and that's the height of the row
that you see in the application as we run it now,
because we don't rows getting unreasonably small.
So we have the new height.
We need to update our model with that new height, so
we do that with I call the self-set height for row.
And now that we've updated our model, we can take
advantage of what Jason said earlier which is
that using an empty update block, we can
actually change the geometry of our table view.
We've already changed the geometry of our model
and now we just need to use an empty update block,
beginUpdates immediately followed by
endUpdates and that will cause the table view
to animate to that -- to the new row heights.
So we call beginUpdates and endUpdates and that's it.
Now, there's one problem with this implementation, and that
is we don't really want to animate to the new positions,
and that's because the gesture recognizer
gives us constant updates as we pinch.
So table view, of course, automatically
and by default animates all the cells
to their new positions when we do an update block.
But, we want to disable that.
We can disable UIView animations with a class method on
UIView, and we'll do that first by finding the current state
of animations enabled, and then we
can set Animations Enabled to No.
And because we're a good citizen, we're
going to restore Animations Enabled
to the value it was before this animation block.
So by doing that, by wrapping our animation block
or rather our update block in Animations Enabled No,
that tells us when we go to the new row heights, that
we've specified and that we've put into our model,
we don't want the table view to actually animate.
That'll just -- it'll go right to that, and that's
fine because we'll be doing this very quickly
and often as the user changes their pinch.
So let's run this.
And now, I can actually pinch if I want
to relieve -- read more about Safari 5.
And as I expand my fingers or my virtual
fingers, the height of the row expands.
And likewise, I can bring it back, and with a pinch,
the user can now change the height of the row.
That's awesome!
[ Applause ]
Thanks. So there's one other way
that we can go at the same idea.
Maybe we want to use the pinch
instead to resize an individual row.
Maybe we want to act as sort of a zoom on the
entire table view and resize all the rows.
So that's a pretty simple code change.
Right now, the row heights are
specified in our model individually.
We have a value in the model for every row's height.
And we can actually just override that by
adding a new ivar that we'll call Row Heights.
And this will override our current model and just
set all row heights, all the heights of every row
to the value that we have in our ivar row heights.
So let's take this, and we'll initialize it, and
I'll end it with Style Method to our default value.
And here, I've got a couple of methods that handle my
model for setting height and getting a height for a row.
And these have some kind of scary looking code that
sets each of the individual row heights in an array.
I'm just going to get rid of that and
make it much simpler so when I set Height,
all I'm going to set is the height of my new ivar.
And likewise, when I query for the height,
I'm just going to turn my new ivar.
And I'm not going to run it again.
We're going give an entirely other effect.
Now when I pinch, every row increases its height.
And I can even go.
And now the user can increase the
size of every row with a simple pinch.
That's pretty cool.
[ Applause ]
OK. Let's look at one last gesture, and
that's going to be a long pressed gesture.
Let's take a look at the app again.
What I want to do is actually add a long pressed gesture
recognizer to the table view cell so that the user can tap
and hold on a cell and will pop up a menu that allows
them to e-mail a story to one of their friends.
So, we're going to go to everyone's favorite
method, TableViewCellForRowatIndexPath.
And when we create a new cell, we're going
to attach a gesture recognizer to it.
So we're going to use the same three
lines you're familiar with by now
in creating a gesture recognizer
and attaching it to the view.
This one's a long press, and we
give it target Self and our action,
in this case is Hand the Long Press,
and we add it to the cell this time.
The entire thing, as you see, is enclosed in a
check to see if this device actually can send mail.
And now I need to implement long press.
So with Handle Pinch, we had to deal with several
different states; began, changed and ended.
With the long press, all we really care about is the
began state because that's we've begun the long press
when the user has tapped and held on something
long enough for it to become a long press.
So all we need to do in this case is very
similar to the pinch gesture recognizer,
we get the location in the table view and
we get that from the gesture recognizer.
And then, again, we query the table
view for the index path at that point.
And now we're just going to fire off a method that
sets up an e-mail menu item for that index path.
And we can see that run now.
We press and hold, and now we get an e-mail menu item.
So that's -- it's another way we can add
a gesture recognizer to another view.
In this case, the cell and gives the user
another way to interact with our table view.
So, we've added three ways for the user to interact with
the table view, and we've done that by gesture recognizers
on three different parts of the table view.
We, again -- we added the tap gesture to the Section
Header View, and that gave the user much greater ability
to tap anywhere in the section, and it's much
more difficult for them to miss their target.
We added a pinch gesture that, of course,
gave them a great new way to interact
with the table view in resizing row heights.
And we also gave them an ability to send an e-mail simply
by long pressing on a table view cell, tapping and holding,
and we've added all this great
interaction with just a little bit of code
and gesture recognizers in the table view.
So I hope that gives you an idea of how you
can use gesture recognizers in your table view.
And thank you for checking out these examples,
and I'll hand the floor to Jason for a conclusion.
Thanks.
[ Applause ]
>> Jason: Thanks, Luke.
Well, if you've never tried to add support like that to an
app before, you have no idea how much of a nightmare it is
to try to add some sort of a complex
gesture recognition on top of something
that already has a bunch of defined behavior.
We did all of that without breaking anything --
what table view does normally scrolling,
selecting any of that kind of stuff.
If you tried to do this the old way with touch handling,
it's really easy to just break some bit other functionality
in the amount of state that you
need to track is really quite huge.
So if you have any more information, our
Application Frameworks Evangelist is Bill Dudney.
His contact info's up here.
There's a bunch of documentation on the table view and
there's some great forums where a bunch of us participate,
and there's a lot of other engineers like you
guys who can help each other answer questions.
We referred yesterday -- or to the
sessions yesterday on gesture recognizers.
If you didn't see them, I highly
encourage you to take a look at them.
Some great content there.
So, in summary, definitely use animated updates to your
table views and play around with gesture recognizers.
See if you can come up with some interesting
ways for users to interact with your content.