Mobile App Development

Technology

Tips

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 www.raywenderlich.com. 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:

Copy
let longpress = UILongPressGestureRecognizer(target: self, action: "longPressGestureRecognized:")
tableView.addGestureRecognizer(longpress)

Now, add the longPressGestureRecognized function with the follow code:

Copy
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:

Copy
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:

Copy

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

Now add this code below that:

Copy
switch state {
case UIGestureRecognizerState.Began:
if indexPath != nil {
Path.initialIndexPath = indexPath
let cell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell!
My.cellSnapshot  = snapshopOfCell(cell)
var center = cell.center
My.cellSnapshot!.center = center
My.cellSnapshot!.alpha = 0.0
tableView.addSubview(My.cellSnapshot!)
 
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:

Copy
func snapshopOfCell(inputView: UIView) -> UIView {
UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
inputView.layer.renderInContext(UIGraphicsGetCurrentContext())
let image = UIGraphicsGetImageFromCurrentImageContext() as UIImage
UIGraphicsEndImageContext()
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:

Copy

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():

Copy

default:
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 = cell.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!.removeFromSuperview()
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...

7

Feb.

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

2

Oct.

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

29

Jan.

Ben Spencer

Dev Terminology 101: Common Programming Languages (and their key frameworks)

Language enables human communication – spoken or written – with words and phrases structured in a conventional way. Just like we need different languages to communicate with one another, we also need different languages to communicate with computers. In an article on Stack Overflow, Matt Sherman writes that at a foundational level, all programming languages … Continued