Implementing drag & drop for Collection View
A quick loot at implementing drag and drop capabilities in your app using Collection View.
Published: July 13, 2021 Sponsored App StoreWhile working on SwitchBuddy for iPad I got the idea for drag & drop which would allow users to open the app with Files for example side by side and then drag images from my app to the Files app.
Turns out getting basic drag & drop is not complicated. In this post, we will look at minimal implementation you can use as a starting point.
Collection View with drag & drop
To implement drag and drop functionality for UICollectionView
we have to implement provided delegate methods.
These are part of UICollectionViewDragDelegate
protocol and collection view has the dragDelegate
property we can set our view controller to.
collectionView.dragDelegate = self
Since I needed just the "drag" feature to allow users drag images out of my app and not vice versa, I had to implement just a single method.
This one:
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
}
Your job is to somehow return an array of UIDragItem
which in turn is a wrapper around NSItemProvider
which wraps the actual data from our app.
NSItemProvider
supports a handful o data types out of the box, for example UIImage
, UIColor
, strings and urls. You can also pass file url when creating the object.
So for the image, implementation of the method above can look like this:
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
let provider = NSItemProvider(object: image)
provider.suggestedName = photo.fileName
let dragItem = UIDragItem(itemProvider: provider)
return [dragItem]
}
I have omitted the part of implementation which gets the image
, since this is going to be pretty specific for your app. In my case, since I am loading images from urls into the cells, I first get a cell, cast it to correct type and then get the image form image view.
Also note the provider.suggestedName
line which lets you control the filename Files and other app will use when saving dropped files.
For my app, this was enough. As I said, I care only about the "drag" part because it does not make sense to drag images into my app.
Implementing "drop"
In case you want to have both drag and drop, here is a short paragraph about how to implement this.
First you need to conform to UICollectionViewDropDelegate
- assuming you want to perform the drop onto collection view. And set it:
collectionView.dropDelegate = self
And then there is single required method to implement:
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
}
Your job is to look at the coordinator
object and decide how to handle the drop. This is pretty complex and greatly depend on the result you want.
There are a few properties you should check.
session
is the UIDropSession
and can tell you, for example which items are dropped using the hasItemsConforming
method.
items
contains the dropped items. (Surprising, I know). They are of type UICollectionViewDropItem
and in turn wrap UIDragItem
with the dragItem
property, which finally has the itemProvider
(NSItemProvider
).
destinationIndexPath
- this informs you where the item(s) have been dropped by the user.
proposal
- this is UICollectionViewDropProposal
with intent
property. This tells you if dropped items should be inserted (.insertAtDestinationIndexPath
) or incorporated (=combined) (.insertIntoDestinationIndexPath
). Again this shows just how much depends on the experience you want and on the drop source.
I think for the simple cases, you can ignore most of these properties and just load the items.