Mobile App Development



How to Create Drag and Drop Cells in Swift using an UILongPressGestureRecognizer

January 23, 2015

Dan Fairbanks

By Dan Fairbanks

While working on a recent project for a client, I needed to implement a drag and drop table view for moving cells around. As I did some research, I found a great tutorial on The only problem was that it was in Objective-C and I was writing my project in Swift.

I couldn’t just simply translate the syntax and have it all working properly because of differences between the two. So, I created this tutorial on how to create a drag and drop UITableView in Swift using a UILongPressGestureRecognizer.

I’m going to assume that you know how to make a UITableView and fill it with data. If not,  I have provided a starter project with an UITableViewController filled with some data from an Array. You can download the starter project here.

The first thing you need to do is add a gesture recognizer to the tableView.

In ViewDidLoad() add the following code:

let longpress = UILongPressGestureRecognizer(target: self, action: "longPressGestureRecognized:")

Now, add the longPressGestureRecognized function with the follow code:

func longPressGestureRecognized(gestureRecognizer: UIGestureRecognizer) {

Inside the longPressGestureRecognized() function, start by getting the location of the gesture in the table view and the corresponding tableViewCell.

Add the following code inside the longPressGestureRecognized() function:

let longPress = gestureRecognizer as UILongPressGestureRecognizer
let state = longPress.state
var locationInView = longPress.locationInView(tableView)
var indexPath = tableView.indexPathForRowAtPoint(locationInView)

Next, create a switch statement beginning with UIGestureRecognizerState.Began. Check that the index path is not nil. If not, then take a snapshot of the cell with a forthcoming method, add the snapshot of the cell to the view and hide the cell.

But first, create some variables for the cell snapshot and the initial index path. These variables need to be structs so that we can access their values later in the code. (This is something that wasn’t necessary in Objective-C.) Add the following code to the bottom of the longPressGestureRecognized() function:


struct My {
static var cellSnapshot : UIView? = nil
struct Path {
static var initialIndexPath : NSIndexPath? = nil

Now add this code below that:

switch state {
case UIGestureRecognizerState.Began:
if indexPath != nil {
Path.initialIndexPath = indexPath
let cell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!
My.cellSnapshot  = snapshopOfCell(cell)
var center =
My.cellSnapshot!.center = center
My.cellSnapshot!.alpha = 0.0
UIView.animateWithDuration(0.25, animations: { () -> Void in
center.y = locationInView.y
My.cellSnapshot!.center = center
My.cellSnapshot!.transform = CGAffineTransformMakeScale(1.05, 1.05)
My.cellSnapshot!.alpha = 0.98
cell.alpha = 0.0
}, completion: { (finished) -> Void in
if finished {
cell.hidden = true


To add the snapshot function, enter the following code below the longPressGestureRecognized() function:

func snapshopOfCell(inputView: UIView) -> UIView {
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
let image = UIGraphicsGetImageFromCurrentImageContext() as UIImage
let cellSnapshot : UIView = UIImageView(image: image)
cellSnapshot.layer.masksToBounds = false
cellSnapshot.layer.cornerRadius = 0.0
cellSnapshot.layer.shadowOffset = CGSizeMake(-5.0, 0.0)
cellSnapshot.layer.shadowRadius = 5.0
cellSnapshot.layer.shadowOpacity = 0.4
return cellSnapshot

Next , we need the logic for when the user’s finger moves. If the indexPath is different than the initial indexPath, swap cells and update the data source.

As part of the next piece of the switch statement in longPressGestureRecognized(), add the follow code:


case UIGestureRecognizerState.Changed:
var center = My.cellSnapshot!.center
center.y = locationInView.y
My.cellSnapshot!.center = center
if ((indexPath != nil) && (indexPath != Path.initialIndexPath)) {
swap(&itemsArray[indexPath!.row], &itemsArray[Path.initialIndexPath!.row])
tableView.moveRowAtIndexPath(Path.initialIndexPath!, toIndexPath: indexPath!)
Path.initialIndexPath = indexPath

Finally, clean everything up with some nice animations. For this, use the switch statement default.

Add the following code to the last part of the switch statement in longPressGestureRecognized():


let cell = tableView.cellForRowAtIndexPath(Path.initialIndexPath!) as UITableViewCell!
cell.hidden = false
cell.alpha = 0.0
UIView.animateWithDuration(0.25, animations: { () -> Void in
My.cellSnapshot!.center =
My.cellSnapshot!.transform = CGAffineTransformIdentity
My.cellSnapshot!.alpha = 0.0
cell.alpha = 1.0
}, completion: { (finished) -> Void in
if finished {
Path.initialIndexPath = nil
My.cellSnapshot = nil

That’s it. Test out your code for bugs. If you would like to see my source code for the complete project then go here.


UPDATE: See the comments below for updates to the code above!

Dan Fairbanks

Dan Fairbanks

I am eternally optimistic and believe that the best way to predict the future is to invent it. When I am not programming insanely great iOS apps, you can find me hanging out with my wife and son, reading a book, crossfitting, wakeboarding, or playing in the mountains.

Unless otherwise specified, source code in this post is licensed under a
Creative Commons Attribution 4.0 International license (CC BY 4.0).

You might also like...



James Lorentson

Autocomplete: Varieties, Benefits & UX Best Practices

For just about everything besides scenic drives, people prefer shortcuts. And if a shortcut not only gets you there quicker but also takes you to a better destination, now that’s something! This is what autocomplete does. By giving users the option of completing words and forms based on what they’ve typed before, it shortcuts their … Continued



Jeff Dance

10 Factors for Choosing a CMS

There are dozens of Content Management System (CMS) platforms available to to help you manage the content, marketing, and SEO on your website. But with all the good options out there, how do you know how to choose the right CMS? Consider the following 10 factors when choosing your CMS. #1 Price Some CMS licenses start … Continued



Michael Wiggins

How Will the Oracle Java Licensing Changes Affect You?

Earlier this year, Oracle announced that beginning January 1, 2019, it will no longer provide support and updates to Java SE 8. Instead, the support and updates that have been included as part of the Java license will now be available only through a separate subscription support service. As we are now well into the … Continued