I\'m wondering if anyone has a link to a good tutorial or can point me in the right direction to recreate the \'drag to reorder\' cells in a UITableView like Epic Win App. The
This solution here will function similar to the Reminders app on OSX which allows you to reorder cells by holding and dragging anywhere.
I've created a subclass of UITableView that allows you to drag and reorder the cells without having to have editing
on or use the standard reorder handles.
The basic idea is I use the touches began and touches ended to get which cell was tapped, I then create a fake view that is really just a screenshot of the view you tapped and move it back and forth, swapping the cells in the background. When the user lets go I then unhide the original view.
class ReorderTableView: UITableView {
var customView:UIImageView?
var oldIndexPath:NSIndexPath?
override func touchesBegan(touches: Set, withEvent event: UIEvent?) {
guard let touch1 = touches.first else{
return
}
oldIndexPath = self.indexPathForRowAtPoint(touch1.locationInView(self))
guard (oldIndexPath != nil) else{
return
}
let oldCell = self.cellForRowAtIndexPath(self.oldIndexPath!)
customView = UIImageView(frame: CGRectMake(0, touch1.locationInView(self).y - 20, self.frame.width, 40))
customView?.image = screenShotView(oldCell!)
customView?.layer.shadowColor = UIColor.blackColor().CGColor
customView?.layer.shadowOpacity = 0.5
customView?.layer.shadowOffset = CGSizeMake(1, 1)
self.addSubview(customView!)
oldCell?.alpha = 0
}
override func touchesMoved(touches: Set, withEvent event: UIEvent?) {
guard let touch1 = touches.first else{
return
}
let newIndexPath = self.indexPathForRowAtPoint(touch1.locationInView(self))
guard newIndexPath != nil else{
return
}
guard oldIndexPath != nil else{
return
}
if newIndexPath != oldIndexPath{
self.moveRowAtIndexPath(oldIndexPath!, toIndexPath: newIndexPath!)
oldIndexPath = newIndexPath
self.cellForRowAtIndexPath(self.oldIndexPath!)!.alpha = 0
}
self.customView!.frame.origin = CGPointMake(0, touch1.locationInView(self).y - 20)
}
override func touchesEnded(touches: Set, withEvent event: UIEvent?) {
self.customView?.removeFromSuperview()
self.customView = nil
guard (oldIndexPath != nil) else{
return
}
self.cellForRowAtIndexPath(self.oldIndexPath!)!.alpha = 1
}
func screenShotView(view: UIView) -> UIImage? {
let rect = view.bounds
UIGraphicsBeginImageContextWithOptions(rect.size,true,0.0)
let context = UIGraphicsGetCurrentContext()
CGContextTranslateCTM(context, 0, -view.frame.origin.y);
self.layer.renderInContext(context!)
let capturedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return capturedImage
}
}
In the end you can reorder cells without having to use the handles. This can be extended for different gestures, or modes depending on the use case, but I left this as a vanilla version. Let me know if you have any questions.
Update:
Some caveats I found. It is very important you figure out how you want to use scrolling and modify this accordingly. For instance, when I mocked it out I set scrollingEnabled = false.
In the long run I did want scrolling, so I changed the code to only run this functionality on a press longer than 0.25 (I used some timers to do this). I would then temporarily disable scrolling during this period, and re-enable upon completion.