UICollectionView Tutorial: Headers, Selection and Reordering
Learn how to implement reusable views for UICollectionView section headers, select cells and reorder with drag and drop. By Fabrizio Brancati.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
UICollectionView Tutorial: Headers, Selection and Reordering
30 mins
- Getting Started
- Adding Custom Section Headers
- Creating a UICollectionReusableView Class
- Setting the Section Header View Class
- Connecting the Section Header to Data
- Interacting With Cells
- Providing Selection Feedback
- Loading the Large Image
- Selecting Multiple Cells
- Keeping Track of Sharing
- Adding a Share Button
- Reordering Cells
- Implementing Drag Interactions
- Implementing Drop Interactions
- Making the Drop
- Where to Go From Here?
Implementing Drag Interactions
Click File ▸ New ▸ File and select Swift File. Click Next, name it FlickrPhotosViewController+DragAndDropDelegate and click Create.
Replace the contents of the file with the following:
import UIKit
// MARK: - UICollectionViewDragDelegate
extension FlickrPhotosViewController: UICollectionViewDragDelegate {
func collectionView(
_ collectionView: UICollectionView,
itemsForBeginning session: UIDragSession,
at indexPath: IndexPath
) -> [UIDragItem] {
// 1
let flickrPhoto = photo(for: indexPath)
guard let thumbnail = flickrPhoto.thumbnail else {
return []
}
// 2
let item = NSItemProvider(object: thumbnail)
// 3
let dragItem = UIDragItem(itemProvider: item)
// 4
return [dragItem]
}
}
collectionView(_:itemsForBeginning:at:)
is the only required method for this protocol, and it’s also the only one you need for this feature. Here’s what it does:
- First, you fetch the thumbnail of the selected photo from the photo object.
- Create an
NSItemProvider
object referencing the thumbnail. This is used by the drag system to indicate what item is being dragged. - Then, create a
UIDragItem
that represents the selected item to be dragged. - The method must return an array of
UIDragItem
objects, so you add thedragItem
created above in an array.
Next, you need to let the collection view know it can handle drag interactions. Open FlickrPhotosViewController.swift and implement viewDidLoad()
above shareButtonTapped(_:)
:
override func viewDidLoad() {
super.viewDidLoad()
collectionView.dragInteractionEnabled = true
collectionView.dragDelegate = self
}
In this method, you set the collectionView’s dragInteractionEnabled
property to true
and set the dragDelegate
.
Now’s a great time to build and run. Perform a search, and now you’ll be able to long-press a cell to see the drag interaction. You can see it lift and can drag it around. But, you won’t be able to drop it anywhere just yet.
Next up, it’s time to implement drop behavior.
Implementing Drop Interactions
Open FlickrPhotosViewController+DragAndDropDelegate.swift. Now, you need to implement some methods from UICollectionViewDropDelegate
to enable the collection view to accept dropped items from a drag session. This will also allow you to reorder the cells by taking advantage of the provided index paths from the drop methods.
Create another extension to conform to UICollectionViewDropDelegate
:
extension FlickrPhotosViewController: UICollectionViewDropDelegate {
func collectionView(
_ collectionView: UICollectionView,
canHandle session: UIDropSession
) -> Bool {
return true
}
}
Typically in this method, you would inspect the proposed drop items and decide whether you wanted to accept them. Because you’re enabling drag-and-drop for only one item type in this app, you simply return true
here. Don’t worry about the compiler error; you’ll fix it shortly.
To update the source data array with changes, you need to make it mutable. Open FlickrSearchResults.swift and update searchResults
as follows:
var searchResults: [FlickrPhoto]
Before you implement the next method, open FlickrPhotosViewController+Helper.swift and place these methods right under photo(for:)
:
func removePhoto(at indexPath: IndexPath) {
searches[indexPath.section].searchResults.remove(at: indexPath.row)
}
func insertPhoto(_ flickrPhoto: FlickrPhoto, at indexPath: IndexPath) {
searches[indexPath.section].searchResults.insert(
flickrPhoto,
at: indexPath.row)
}
These methods allow removing and inserting photos into the searchResults
array.
Making the Drop
Open FlickrPhotosViewController+DragAndDropDelegate.swift and implement the following method inside the UICollectionViewDropDelegate
extension:
func collectionView(
_ collectionView: UICollectionView,
performDropWith coordinator: UICollectionViewDropCoordinator
) {
// 1
guard let destinationIndexPath = coordinator.destinationIndexPath else {
return
}
// 2
coordinator.items.forEach { dropItem in
guard let sourceIndexPath = dropItem.sourceIndexPath else {
return
}
// 3
collectionView.performBatchUpdates({
let image = photo(for: sourceIndexPath)
removePhoto(at: sourceIndexPath)
insertPhoto(image, at: destinationIndexPath)
collectionView.deleteItems(at: [sourceIndexPath])
collectionView.insertItems(at: [destinationIndexPath])
}, completion: { _ in
// 4
coordinator.drop(
dropItem.dragItem,
toItemAt: destinationIndexPath)
})
}
}
This delegate method accepts the drop items and performs maintenance on the collection view and the underlying data storage array to properly reorder the dropped items. You’ll see UICollectionViewDropCoordinator
referred to here. This UIKit
class gives you more information about the proposed drop items.
Here’s what happens, in detail:
- Get
destinationIndexPath
from the drop coordinator. - Loop through the items — an array of
UICollectionViewDropItem
objects — and ensure each has asourceIndexPath
. - Perform batch updates on the collection view, removing items from the array at the source index and inserting them in the destination index. After completing that, perform deletes and updates on the collection view cells.
- In the completion handler, perform the drop action.
Open FlickrPhotosViewController.swift, find viewDidLoad()
and add the following code to the end of the method:
collectionView.dropDelegate = self
You simply set the dropDelegate
.
You’re doing great! Just the last step to go now! Open FlickrPhotosViewController+DragAndDropDelegate.swift and implement the following method at the bottom of the UICollectionViewDropDelegate
extension:
func collectionView(
_ collectionView: UICollectionView,
dropSessionDidUpdate session: UIDropSession,
withDestinationIndexPath destinationIndexPath: IndexPath?
) -> UICollectionViewDropProposal {
return UICollectionViewDropProposal(
operation: .move,
intent: .insertAtDestinationIndexPath)
}
The UIKit drop session calls this method constantly during interaction to interpolate where the user is dragging the items and to give other objects a chance to react to the session.
This delegate method causes the collection view to respond to the drop session with UICollectionViewDropProposal
that indicates the drop will move items and insert them at the destination index path.
Great! It’s time to build and run.
You now see a drag session coupled with some nice behavior in your collection view. As you drag your item over the screen, other items shift to move out of the way, indicating the drop can be accepted at that position. You can even reorder items between different search sections!
Where to Go From Here?
Congratulations! You’ve just finished a whirlwind tour of some pretty advanced UICollectionView
features. :]
Along the way, you learned how to delineate sections with reusable headers, select cells, update cell layout, perform multiple selections, reorder items and much more. You can download the final project by clicking the Download Materials button at the top or bottom of this tutorial.
You can also find an interesting video course about UICollectionView
at Collection Views.
If you have any questions or comments about UICollectionView
or this tutorial, please join the forum discussion below.