iOS. Apple. Indies. Plus Things.

Using UIDragPreview to Customize Drag Items

// Written by Jordan Morgan // Feb 3rd, 2022 // Read it in about 1 minutes // RE: Snips

This post is brought to you by Emerge Tools, the best way to build on mobile.

let concatenatedThoughts = """

Welcome to Snips! Here, you'll find a short, fully code complete sample with two parts. The first is the entire code sample which you can copy and paste right into Xcode. The second is a step by step explanation. Enjoy!


The Scenario

Change how your dragged items appear during an active drag.

Note:This snip assumes you’ve already set up drag and drop reordering, which you can get a full code sample for here.

extension PreviewParametersViewController: UITableViewDragDelegate {
    // 1
    func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
        guard let item = datasource.itemIdentifier(for: indexPath) else {
            return []
        let itemProvider = NSItemProvider(object: item.id.uuidString as NSString)
        let dragItem = UIDragItem(itemProvider: itemProvider)
        dragItem.localObject = item
        // 2
        guard let cell = tableView.cellForRow(at: indexPath) else { return [dragItem] }
        let cellInsetContents = cell.contentView.bounds.insetBy(dx: 2.0, dy: 2.0)
        // 3
        dragItem.previewProvider = {
            // 4
            let dragPreviewParams = UIDragPreviewParameters()

            dragPreviewParams.visiblePath = UIBezierPath(roundedRect:cellInsetContents, cornerRadius: 8.0)
            // 5
            return UIDragPreview(view: cell.contentView, parameters: dragPreviewParams)

        return [dragItem]

Now, when you drag to reorder you’ll see we get a nice, rounded view of the cell that’s being dragged instead of the default square edges:

Demo of a dragged cell with customized rounded corners.

The Breakdown

Step 1
Both UICollectionView and UITableView have protocol methods that look similar for returning drag items, i.e. itemsForBeginning.... The one for table view is this one:

func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem]

Here’s where we can customize dragged views.

Step 2
Since we want to base our customized drag preview off of the cell contents itself, first we make sure that we can get a reference to it. This is important because of cell recycling. For example, the one we’re after might already be offscreen if the user scrolled up or down beyond it.

Step 3
Each UIDragItem has a previewProvider closure that allows us to return a UIDragPreview instance. This last object allows us to customize the dragged item.

Step 4
Using the visiblePath property of UIDragPreviewParameters, we can give it a UIBezierPath based off of the cell’s content frame, shrunk down a bit and with a rounded corner. Preview parameters work together with a drag preview to determine how the drag will look.

Step 5
Finally, we return the UIDragPreview that uses our UIDragPreviewParameters from the closure and we’re good to go!

Until next time ✌️


Spot an issue, anything to add?

Reach Out.