Drag and Drop with Collection and Table View

Session 223 WWDC 2017

Discover new APIs for UICollectionView and UITableView designed to make drag and drop easy to implement in your apps. Learn how to quickly adopt drag and drop in collection and table views, create polished animations, and adopt new features to keep your app fluid and responsive during asynchronous data transfers.

[ Applause ]

Good morning.

Welcome to Drag and Drop with collection and table view.

I'm Tyler Fox.

I'll later be joined by my colleague Mohammed Jisrawi.

And unfortunately our co-presenter Steve Breen had a last minute medical emergency earlier this week, so he's been an amazing trooper and has been recovering quickly but unfortunately can't join us on stage today.

So just a quick round of applause, because he contributed a lot to the stuff we're going to talk about today.

So with that, let's talk about Drag and Drop in collection and table views.

We built a nice little app to showcase some of the ways that you can use Drag and Drop in collection and table view.

In this app, it's a nice photo library where we have albums and photos in each of those albums, and you can drag photos around from one album to another.

You can even actually drop them directly into that table view of each of the albums.

And you can reorder photos within the actual collection views.

So we're going to show you how you can actually use our new APIs and iOS 11 to add Drag and Drop to collection and table views in all of your apps.

Now, you've probably been to some of the sessions earlier this week about all of this new great API that we have in UIKit for Drag and Drop.

So we have this great new drag interaction and drop interaction API layer.

We have new enhancements to NS item provider in iOS 11, but today, we're going to focus on UICollectionView and UITtableView, of course.

And we have new higher level APIs specific for those classes.

So you might be wondering why do we need new APIs just for collection and table view?

It's a great question.

We wanted to build some specific APIs that are focused around cells and index paths.

As you know, collection and table view are composite views that build up a lot of smaller cells to display on screen.

And so we realized we can help you out a lot by speaking in those native cell and index path terms that you're familiar with.

We also wanted to make it easy to build fluid animations and get consistent behavior with your collection and table views across the system.

And we also recognized there are some specific challenges with collection and table view to handle a synchronous data loading.

We have some really cool new stuff that we're really excited to share with you later in the session.

Finally I'd like to make a note that throughout this talk we're going to be showing examples of collection view and table view code but you should know that we have a consistent API for both of these classes, so sometimes we're going to show one or the other and you should know that [inaudible] some small naming differences, everything applies equally to both.

We'll call out the differences when there are some.

With that, let's go through our agenda for today.

We're going to start by covering the basics.

How you can very quickly just get Drag and Drop up and running in collection and table views.

And then we're going to focus on perfecting drops.

With just a little bit more work, you can make your Drag and Drop experience in collection and table view just look awesome.

And we'll show you some really cool animations you can add and a whole bunch of great stuff.

And finally we'll talk about final touches, some really nice customizations and additional things that you can take advantage of to make that experience really shine.

To start, I'd like to bring up Mohammed to tell you all about the basics of getting Drag and Drop in your collection and table views.


[ Applause ]

Thanks Tyler.

We wanted to make adopting Drag and Drop in your apps with collection and table view as easy as possible.

So to help you do that we're introducing two new delegates on both collection and table view.

The first is the dragDelegate, which has methods for initiating and customizing drags.

And second is the dropDelegate, which has methods for completing drags.

So basically data transfer, customizing drop animations, things like that.

It's important to note that these two protocols are completely independent.

You can use one or the other, if you want one-half of the experience or you can use both to have both Drag and Drop.

Using both also unlocks additional functionality like reordering within your collection or table view.

Implementing drags is pretty straightforward.

You conform to dragDelegate and implement one required method.

Items for beginning session at index paths is called when the user initiates a drag out of your collection view, so basically when they start dragging a cell.

It's the app's responsibility to use the input session and index path to figure out how to handle that drag.

So returning an array of UIDragItems initiates the drag, whereas returning an empty array causes the drag to be ignored.

You might remember that tapping other items, or other views, in your app to add them to a drag that's already in progress is a key experience of Drag and Drop on iOS.

Opting into this behavior in collection and table view is really easy.

There's an optional method on dragDelegate that's called whenever you tap a cell in the collection view while a drag session's already in progress.

Items for adding to session at index path point gives you the opportunity to return drag items to add to your in-progress drag.

Returning an empty array from this method causes the tap to be handled normally.

So it could be interpreted as a cell selection, for example.

Accepting a drop is equally easy.

Similar to the dragDelegate, there's a single required method to get you started.

Perform drop with coordinator gets called when the user lifts their finger over your collection view releasing the dragged items.

This method gives you the opportunity to handle the drop using the drop coordinator.

The drop coordinator provides a lot of convenience methods for you for doing things like accessing the dropped items so you can actually perform the data transfer.

It gives you some really great convenience methods for updating your collection and table view.

And finally it gives you some ways to specify some default system animations for your drop.

So now that we've talked about how easy it is to adopt Drag and Drop in your collection table views, let's take a look at it with a demo.

So I'm going to start out with a basic gallery application that uses a collection view to display a grid of photos.

And we're going to add the ability to drag images out of the collection view and to import images back in by dropping them from other apps.

So, as you see, I've already started with kind of a stub implementation of itemsForBeginning Session at IndexPath.

And I'm just going to add some basic code that goes to our data source, retrieves a photo model object, and then pulls out a UI image from this photo using our image property.

Since UI image already conforms to NSItemProvider writing, we can just use it to create an NSItemProvider.

For your own model object, if you're dragging something other than images, you'll have to make sure that those objects conform to NSItemProvider writing.

There's a great session about that just after this.

Once we have the NSItemProvider, we can use it to create a UIDragItem which will return to the system to start the drag.

So I'm good to go to start dragging things, but I also want to add the ability to flock things to my in progress drag, so I'm going to add an implementation of items for adding to session at IndexPath, point.

And I'm just going to do the same thing that I did in my itemsForBeginning Session method to just return a bunch of drag items.

So let's build this real quick and see what it looks like.

So I'm going to pull up the photos app and pin it next to my gallery app.

You'll see that I can now start dragging things out of my collection view, and, if I tap some of these other cells, they flock over to my in-progress drag, and I can carry them over to photos and let go to drop.

So now that we have dragging items, let's look at adding accepting drops.

So, as we discussed earlier, we'll have to implement the one required method on collection view dropDelegate, performDropWith Coordinator.

Here we're going to take advantage of the coordinator's convenience methods to get some information about the drop and then actually perform it.

The first thing we'll want to do is ask the coordinator for the destination index path.

This IndexPath is derived from the point at which the user releases the dragged items.

Since that point could be over a region of the collection or table view that's completely empty, it could be nil.

So we'll just handle that case here by returning a zero zero and inserting it at the inserting it at the beginning of our collection view.

Next, we're going to take advantage of a convenience method on the drop session to load all objects of a specific class in the scenario UI image.

That's what we're interested in.

And then in load objects' completion closure we'll update our collection view, like we normally would, by calling perform match updates with some data source updates using a convenience method that we've written in the past.

And we'll ask the collection view to reload the first section.

So let's give that a quick try.

So now I'm going to pick up a couple of these photos from the photo's app and I'm going to just drop them over my collection view to accept the drop.

So I can pick up some items from photos actually this time.

Go back over to my app and let go to accept the drop.

[ Applause ]

Thank you.

So now that we've actually seen what it looks like [laughter], just by adding a couple of methods, it was easy to get started.

Now Tyler is going to talk about how we can build some really awesome drop animations.

Thank you.

[ Applause ]

Thanks Mohammed.

All right.

So let's turn our attention to really focus in now on perfecting these drops.

And I'll start by talking about something called a drop proposal.

So if you've been to some of the earlier sessions on the other APIs that are available in UIKit, this might be familiar.

But a drop proposal is your way to communicate to the system how you'd like to handle the drop session before the user actually releases their touch to perform the drop.

The system is going to ask you to create and return a drop proposal multiple times while the user is dragging over the collection or table view.

Now collection and table view, each have subclasses of UIDropProposal, and, as a result, that means they each have that same property that comes from UIDropProposal, which is an operation.

So this is a value like copy, move, cancel, or forbidden, and generally indicates whether or not you're interested in this particular drop session and how you intend to handle the actual data inside of that drop session.

But collection and table view drop proposals don't stop there.

They also have an additional value, which is a drop intent.

What is this?

This is some additional information that your app can provide to the collection or table view which will let the collection or table view actually update their appearance during the drop session to reflect what you intend to do.

So let's look at some of the actual values for this drop intent to understand it better.

We will start with the default one, which is the drop intent unspecified.

With this drop intent, collection or table view won't actually change their appearance at all during the drop session.

As you can see here, when the user's dragging around over it, there's just a nice little badge next to the drag preview and that's it.

You might choose this drop intent if you don't yet know, for example, where the items will end up when the user actually realizes their finger to perform the drop.

Or perhaps the drag session contains a mix of items, where they're going to end up in different locations when the actual drop occurs.

So that's the first drop intent.


The second drop intent is insertAtDestinationIndexPath.

With this drop intent, it means that you intend to insert a new item or row into the collection or table view at the destination index path.

And, as you can see, collection and table view will actually open up a gap at this location to let the user know that's the location where their drop will be accepted.

The third drop intent is insertIntoDestinationIndexPath.

This is something that you want to use when the user is dragging over a cell that represents some sort of container of items.

For example, here we have photo albums, containers of photos, or maybe you have a folder cell or a list cell.

In this case, you intend to insert the dropped items inside of that container.

And so, table view, for example, is going to highlight the row at the destination index path so the user knows that the drop will be accepted inside that row.

Now there's one more drop intent value which is specific to UITableView and that's the automatic drop intent.

This is something that you should use instead of insertIntoDestinationIndexPath.

And this drop intent means that you can either drop into an existing container row at that index path or you could move the container row down out of the way and insert a new row into the table view at the same index path.

And table view is going to automatically tell you, when the drop occurs, which one it is.

It'll pick between insertAtDestinationIndexPath and insertIntoDestinationIndexPath for you.

So let's look at an example of how you can provide a drop proposal in code.

It starts by implementing this optional method, dropSessionDidUpdate, withDestinationIndexPath.

Here this will be called whenever the table view or collection view detects that there's a drop session moving over it.

Now this method is optional, but we strongly recommend that you implement it because this is how you provide your drop proposal.

Now because this is called all the time when the collection or table view sees a drop session over it and whenever that drop session moves, it's going to be called very frequently.

And so your implementation needs to be very efficient and return quickly to avoid hanging the main thread.

Now I'd like to call your attention to this destinationIndexPath that's passed in.

Once again, this is our proposal of where we think that you would like to insert any items into the collection or table view.

Now this destinationIndexPath will be nil in some cases.

In particular, when the user's dragging over a region of the collection or table view where there are no cells.

And another very important point about this that might trip you up, so be careful.

When the user drags over the end of an existing section in the collection or table view.

This index path might be equal to the count of the number of items in that section, which means it may not correspond to an existing item in that section.

So be careful because that's an insert at the very end of the section.

So let's look at a quick example of how you could implement this.

In this example, we'll look at the local drag session property on the drop session to see if the drag originated from our app or not.

If it's from our app, we can use a drop operation of move and otherwise we'll use a drop operation of copy.

Both cases here we're going to insert a new item into the collection view, so we'll use insertAtDestinationIndexPath as our intent.

Okay. So that's drop proposals which you provide while the user's still dragging over the collection or table view.

Let's switch gears now to talk about dropping when the user actually releases their touch and you need to perform the drop.

To set up some great animations, you can use the drop coordinator that we pass in to perform drop with coordinator.

Collection and table view will provide some basic animations by default if you do nothing.

But for the best effects, you can use the drop coordinator to set up specific animations for each item independently in the drop session.

Let's look at some of the methods that we've made available for you to do that.

The first one is dropping to an item or row.

This is a drop animation that you'll use with the intent insertAtDestinationIndexPath.

And you want to use this when you've already performed an update to insert a new item or row into the collection or table view.

So let's look at an example in code to see how we can implement this.

Inside of performDropWith coordinator, we need to start by gathering up a few local variables that we need.

In particular, we'll need our destinationIndexPath, and we'll also use the local object property on the drag item to actually get at the underlying data synchronously.

This is something that you can do when the drag session starts from your own app.

Next, because we have that data right away we can perform an immediate insertion into our collection view, so we'll call performBatchUpdates.

And inside, of course, we'll do the usual update of our data source incorporating that new image.

And we'll insert an item in the collection view.

And now, because we've actually already inserted that cell, we can tell the drop coordinator to drop the item to that specific cell at that index path.

This is going to set up that nice animation you saw where the drag preview morphs right to the final cell that we inserted.

All right.

The next drop animation is dropping into an item or row.

As you might imagine, this is generally used with the intent insertIntoDestinationIndexPath.

In this case, we're inserting the item inside of a container cell.

And so the item is going we want the animation to kind of scale down with this nice animation right to some region inside of that cell to show the user where this drop was accepted.

So let's look at an example of how we could set this one up in some code.

Once again, we're back in our performDropWith coordinator and we have basically the same code here that you saw from the previous example.

So just gathering up our destinationIndexPath and the image that actually was in the drag session.

Now there's one additional check that we do want to do, in this case, which is to make sure that that destinationIndexPath corresponds to an actual row in our table view.

Otherwise, we can't drop into anything in that album.

If it does, we can actually incorporate the data inside the underlying data structure for that photo album.

And then we're ready to set up our animation.

So, in this case, we'll get the cell for that particular row and we're going to animate to the image view in that cell.

So we'll get the image view.

Then we call coordinator drop item IntoRowAt index path rect.

We're passing a rect that it's in the coordinate space of the cell, so we translate that image view's bounds up to the cell's coordinate space.

So to recap.

Now, we've talked about two drop animations.

We have dropping to a newly inserted cell.

We have dropping to a rect inside a container cell.

There's a third drop animation method available which is dropping to a target.

This is how you can perform completely custom animations.

For example, animating to a location anywhere in your app with an optional transform.

So maybe you want to animate to a tab bar or bar button item or something like that.

Those are the three basic drop animations.

But I'd like to step back to the first one that we talked about and revisit that just for a second.

Dropping to a newly inserted item or row.

As we've discussed, you actually need to already have performed the update on the collection or table view to insert that new cell before you can use this method.

But what if the data hasn't loaded yet?

If you're dragging between applications on iOS, we always have an asynchronous data load.

But these animations need to be specified immediately before you return from performDropWith coordinator.

So to solve for that, that would mean you'd have to do some pretty difficult bookkeeping, because you're going to have to do temporary insertions in the collection or table view, just to set up these animations.

Which, of course, would mean updating your data source with temporary model objects, and that's not going to be easy.

Remember these asynchronous loads can even come in out of order.

We know this is a big challenge, and we are extremely excited to tell you about brand new technology in iOS 11 for collection and table view, specifically designed for this problem.

I'd like to introduce placeholders.

[ Applause ]

Okay. What are placeholders?

So, as the name suggests, these are temporary items or rows that you insert into your collection or table view.

And the key thing about them is that you can defer updating your data source until the data finishes loading when you insert placeholders.

So for placeholders, you can use any cell that you want.

You can provide it to us.

We'll do all the difficult bookkeeping on the back end until your data finishes loading.

The way this works is that collection and table view will never actually call out to ask your delegate or data source about placeholders, so your delegates can remain blissfully unaware that they even exist.

You can insert as many placeholders as you want, anywhere you want in the collection or table view, and you can even perform incremental updates while placeholders exist to put new rows or items in the collection view, table view, or move some existing one.

Anything like that.

So this provides users with a great experience while their data is loading across applications, because you can keep your user interface live and responsive during these data transfers.

So how do you create placeholders?

Well, you're creating them using the drop coordinator, of course.

To insert and animate it to a placeholder.

Let's dive into some code and see how it's done.

So we're back in our performDropWith coordinator method.

Again, we'll need a destinationIndexPath that we're actually going to drop to.

And now here we do things a little bit differently.

Now we're going to iterate over each of the items in the drop session independently and for each item, we're going to insert a placeholder.

So we call coordinator drop item toPlaceholderInsertedAt index path withReuseIdentifier.

This will actually insert a placeholder at the destinationIndexPath for this particular item.

You'll notice we pass over use identifier.

This is something that you've already registered with the collection or table view and we're going to use that to dequeue a cell for this placeholder.

You'll also notice, of course, that there's a closure at the end and that there's a comment configure your placeholder cell here.

Well, as you recall, collection and table view, they're not going to call out to your delegate or data source about placeholders.

That mean you're not going to get a call for cell for row or cell for item in index path for placeholders, so this closure serves as your replacement for the configuration that you might normally do in cell for row at index path or cell for item in index path.

Okay. There's one more thing.

Once you've inserted the placeholder, it actually returns something back to you, and that's this placeholderContext.

What is this?

The placeholderContext is returned for each placeholder independently.

And this is how you're actually going to commit the insertion of the placeholder to exchange it for the final cell once your data finishes loading.

Or, if the data transfer failed.

Or maybe it was cancelled by the user.

You can use the placeholderContex to actually delete the placeholder because it'll no longer be needed.

So let's jump back to our code and see how we use this placeholderContext.

We're picking up right where we left off.

We inserted our placeholder.

We got the placeholderContext back.

Now we can actually go and load the data.

Again, this is asynchronous, so we use this load object of class method to do that.

When the closure completion handler runs, that'll be called on a background queue.

So we make sure to transition back onto the main queue before we update our UI.

And now we can check did we actually get any data loaded?

If we did, we'll have an image in this local variable here.

And now we call commitInsertion on the placeholder context, which again, we'll swap out that placeholder cell and insert the real cell at this particular location.

Now you pass a closure to commitInsertion.

Inside of that closure, it's your responsibility to update your data source to incorporate the new data for the actual row here.

Or item in the collection view.

Now one other important thing.

Take a look at that index path that's being passed inside that closure, the insertionIndexPath.

You need to be very careful to actually use that one and not the original destinationIndexPath that you originally inserted the placeholder at.

This is because between the time when you inserted the placeholder and when the data finished loading, if other updates happen in the collection or table view, the placeholder might have moved.

And so you use this index path, which we provide to you, to tell you the final location when the placeholder insertion is committed.

Okay, one other thing.

Of course, if our data transfer didn't work for some reason, or maybe it was cancelled, we can just delete the placeholder because we no longer need it.

Now one quick note about working with placeholders.

It's very important that you avoid calling reload data on the collection or table view when you have placeholders.

Instead, you should use performBatchUpdates.

This is so you can perform incremental updates on the collection or table view.

And the reason why you want to avoid reload data is because that just resets everything in the collection or table view.

And, as a result, it's going to strip out any existing placeholders that you still have.

If you use performBatchUpdates, you'll be performing incremental insertions and deletions, and we can keep those placeholders alive while you update your collection view in real time.

You can use the new property hasUncommittedUpdates on collection and table view, if you would like to check if it has placeholders still around.

With that, I'd like to hand it back to Mohammed to show you an amazing demo of all this placeholder goodness in action.


[ Applause ]

Yeah. I can hold it.

Thanks Tyler.

Wow. Placeholders sound amazing, don't they?

I can't wait to actually build them in our app and build a really awesome drop animation.

Let's get started.

So the first thing we'll want to do is implement another optional method on the drop delegate to indicate to the collection view that we'd like to animate our dropped items to the drop point.

So let's add an implementation for dropSessionDidUpdate withDestinationIndexPath.

And we'll start returning a collection view drop proposal with a drop operation of copy and an intent of insertAtDestinationIndexPath.

Next, we'll head back to our performDropWith coordinator method and we're going to remove our existing coordinator code because we're going to do something a little different.

The first thing we'll want to do is iterate the coordinator's drop items or self, and we're going to check if each drop item, drag item item provider can be used to load an object of class image.

If it can, then we'll do a couple of things.

First, we'll ask the coordinator to drop a placeholder at the destinationIndexPath.

And we'll pass it a reuse identifier for a placeholder cell that we've registered with the collection view previously.

We're going to hang onto the placeholder context, because we're going to need it to update the collection view later on.

Next, we'll actually trigger the data load by calling load object on the item provider and loadObjects is completion handler.

We'll go ahead and handle the drop.

Note that I'm dispatching back to the main queue here, since loadObjects is completion handler is called on a background queue, and we're going to be updating our UI.

If the data transfer succeeded, we'll finalize the drop by calling commitInsertion on the placeholderContext.

And we'll pass it a set of data source updates that use the insertionIndexPath to insert our new image in our [inaudible].

Then if the data transfer failed for some reason, we'll make sure to clean things up by calling deletePlaceholder on a context.

There's one more thing we should do here and that's to disable the default system progress UI for long-running data transfers, because we have our own inline one now.

So we'll go ahead and do that by setting the drop session's progress indicator style to none.

That's it.

Let's see what it looks like on our device.

So to get the full effect of what we've just done, I'm going to instead of dragging out a photo, I'm going to switch over to a different app, which I've called Slow Photos, which intentionally slows down data transfers.

I'm going to pick up a few of these images here and I'm going to head back to my collection view.

Notice the first thing you'll notice here is that when I hover over the collection view, the cells start moving apart and we get this cool kind of springboard-like reordering look.

This is because we started returning the insertAtDestinationIndexPath intent.

If I let go of the dropped items oh, I love that animation [laughter].

So note that the first thing we get are the placeholder cells with this inline progress UI.

And a few seconds later, when the data transfer completes, we actually have the cells, the real cells with the real images loaded.

[ Applause ]

So now I'm going to hand it back to Tyler who's going to talk about some final touches that can really help you make the drag and drop experience in our app uniquely awesome.

Okay. Thanks Mohammed.

So let's wrap up by quickly jumping through some of the final touches that you can adopt using our wonderful APIs here.

The first one that might be very interesting to you is supporting reordering.

So now that we have Drag and Drop, it might be obvious that you might want to use that for reordering.

Well you can of course do that and it's really easy.

You start by implementing the dropDelegate method, dropSessionDidUpdate withDestinationIndexPath.

The reason why, of course, that's how you provide your drop proposal to the system.

And, in particular, you need a very specific drop proposal if you like reorder, which is the drop proposal with an operation of move and an intent of insertAtDestinationIndexPath.

That serves as a signal to the collection or table view that you're interested in supporting reordering, when the item that's being dragged comes from the same source view.

Now here's where things get a little bit different for table view and collection view.

We'll start with table view.

With table view, table view has already supported reordering for a very long time and you're familiar with this very longstanding data source method.

Table view moveRowAt IndexPath to IndexPath.

You can continue to implement this, if you like, to support reordering, using Drag and Drop.

Because table view is actually going to call this instead of calling through perform drop with coordinator, if you've returned that magic drop proposal and a single row is actually being reordered.

This makes it really easy, because you can use the same code to ship on an iPhone.

For example, maybe where you don't have Drag and Drop using the existing style of reordering.

And, if you are on an iPad, you can use the new style of reordering here.

So that's table view.

Pretty straightforward.

Collection view is also equally easy, though.

Collection view.

You just need to make sure you're implementing both a drag and drop delegate.

So both sides.

And inside of perform drop with coordinator, you'll actually look at the drop items that we passed to you in that coordinator.

We actually are providing a sourceIndexPath for each of those items, which will be non-nil, if the item represents a particular item in the collection view that it came from.

So you can delete the item at that sourceIndexPath and insert a new item at the destinationIndexPath.

And that's going to affect the actual reorder.

So you'll have that cell move seamlessly from the start to the destination.

Now collection view has some extra functionality for reordering, though, and that's this new concept of reordering cadence.

As you know, collection views often have these two-dimensional grid-like layouts.

For example, the flow layout you see behind me.

And when you're reordering, sometimes you can get the kind of behavior where the items are reflowing out as you're moving your finger.

And that's great when you're actually trying to reorder.

But you don't always want that behavior.

Maybe if you're trying to just drag to a different location outside of the collection view and it happens to support reordering.

So the reordering cadence has three different values to help you tune how responsive the collection view is going to be when it shuffles and reflows its layout.

So the default, of course, is immediate, which you've seen in the demos so far.

Where once you start moving around, it immediately is going to reflow the collection view layout as you're you know, almost in real time reordering.

If you'd like to add a little bit more deliberance here, you can switch to the fast mode.

Where, if you move very quickly, it won't actually immediately reshuffle all the layout around.

And so you can see you have to pause just a little bit longer.

And then for slow, it exaggerates that even further.

So the user really has to be deliberate about wanting to stop and reorder to a specific location.

This is how you're going to get behavior very consistent with the behavior you see on the iOS Home screen when reordering app icons, for example.

So that's reordering.

Let's talk about spring loading.

Spring loading, as you probably know by now, is a way to actually navigate and activate controls throughout the system while you're in the middle of a drag session.

You just hover over the controls.

It starts highlighting and then it's going to activate.

So, in collection and table view, it's dropped at easy to adopt spring loading and that's because both of them conform to the protocol UISpringLoaded InteractionSupporting.

Basically that just means they each have a property isSpringLoaded, which you set to true, and spring loading works.

When users spring load on a particular cell, we're going to call we're going to select that item or row in the collection or table view.

And you can customize spring loading using a new delegate method shouldSpringLoadItemAt IndexPath with context.

This is how you can opt out specific rows of spring loading.

Or you can even customize spring loading effects a bit using that context.

For example, you can change the view that actually flashes during the spring load interaction.

So next, let's talk a little bit about customizing appearance.

We have some new ways that you can customize the appearance of cells that are participating in drag sessions.

So cells start they have a new notion of a drag state, and they all start in the none state, and that's before anything's happened.

But when the user actually puts their finger on the glass and starts lifting one of those cells, we're going to transition the cell to the lifting state.

And, as you can see here, in this example, we have a little banner showing on each of the cells.

And we'd rather not actually show that banner when the user's dragging the cell all around across the system.

So we can use this transition of the state to the lifting state to actually hide that banner.

When the user actually moves their finger to actually begin the drag session, once the cell finishes the lift, we'll transition the cell that remains behind in the collection or table view to the dragging state.

And by default, this is going to result in a faded appearance, where we'll reduce the alpha of that cell.

Now you can respond to these state transitions by overriding the method on your cell class, subclass, dragStateDidChange.

Just use the new state that's being passed in.

You can optionally call super to get the default appearance and behavior.

Or you can just choose to override it and provide your own behavior and implementation.

We will even make sure to call these state transitions inside and alongside animation closure with the actual lift animations, for example.

So if you make changes here, they're going to be animated running forwards and backwards with those lifts.

So that's how we customize the actual cell that is in the collection or table view, but what about the actual drag preview that you drag around?

As you see here, in this example, we have a cell that is basically a square shape.

But, if the content that's visible is a wider aspect ratio, it's showing a nice image and so we result in, you know, these not very pretty white bars at the top and bottom.

Probably don't really want this appearance while the user's dragging this, you know, photo around the system.

So how can we fix that?

Well, by default, it's happening because we use the entire cell as the drag preview.

But, of course, you can change that.

And so you can provide drag preview parameters by implementing an optional method on the dragDelegate, dragPreviewParametersForItemAt IndexPath.

And, if you provide these, there's, for example, an opportunity for you to provide a bezier path, which will clip to a specific region within the cell.

So if we look back at our example, if we return a bezier path that clips to the actual rect where there's a visible photo, we can just lift just that photo off the screen even though the cell is still a square in this example.

[ Applause ]

Okay. Wow.

We've covered a lot today.

So what are the next steps for you?

Well, first of all, we hope when you leave here you go and add Drag and Drop to all the collection and table views in your applications.

You saw how easy it is just to get the basics up and running very quickly.

And users are going to expect that your apps support Drag and Drop in iOS 11.

Now you should definitely consider providing a drop proposal and setting up those great animations, because we made it really easy to do so.

And it will provide the best look and feel for your users.

And, of course, don't forget about placeholders.

When your data is loading asynchronously, use placeholders to very easily manage that asynchronous data load and keep the UI completely responsive and interactive during any long-running transfers.

And finally, don't forget to polish the details.

We can't wait to see what you will do with all of these really awesome APIs, and we're going to be super excited to see the results.

For more information and to download the sample app that we've been showing you today, which has fleshed-out examples of all sorts of great Drag and Drop with collection view and table view code.

Go to this URL, it's really great.

There's a session immediately following this one, in this room right here, so you can just stay seated.

Data delivery with Drag and Drop, which is going to go into some more details about loading and transferring the actual data and item providers.

Really cool.

Stick around for that.

And, of course, we had some other sessions earlier this week on Drag and Drop, introducing Drag and Drop, and mastering Drag and Drop.

Check those out on video to learn more.

Finally, we hope you've enjoyed the presentation and have a great WWDC.

[ Applause ]

Apple, Inc. AAPL
1 Infinite Loop Cupertino CA 95014 US