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...

30

Jan.

Marc Wallace

Machine Learning UI Starts with Trust

With the increasing amount of data, the ability to detect patterns and surface them in a trustworthy and actionable manner is key to remaining competitive. Let’s start by defining a couple things first: UI (User Interface) is the means by which the user and a computer system interact. For the purpose of this article we … Continued

28

Jan.

Jeff Alexander

The Value of Robot-Centered Design

Designers, developers, and engineers have spent vast amounts of time and effort understanding how humans interact with technology. Human-Centered Design The study of human factors guides our knowledge of how our behaviors, beliefs, education level, and past experiences influence how we accept and work with digital tools. Those who create computers, mobile devices, software, hardware, … Continued

21

Jan.

Jeff Dance

Creating High-End Applications by Blending Functionality and Emotion

Application design should focus on creating an experience that users remember. The key question to ask is why they will remember it. Is it emotion that invests your users? Or does functionality play a significant role as well? Functionality should always be placed front and center: It ensures that your application works. But that’s not … Continued