This solution will function in the same way as the OSX reminder application, which allows you to reorder cells by holding and dragging anywhere.
I created a subclass of UITableView that allows you to drag and reorder cells without having to have editing or use standard reordering descriptors.
The basic idea is that I use the started strokes and contacts to get which cell was pressed. Then I create a fake view, which is actually a screenshot of the view you are viewing, and moves it back and forth, replacing the cells in the background. When the user allows the transition, I will then show the original view.
class ReorderTableView: UITableView { var customView:UIImageView? var oldIndexPath:NSIndexPath? override func touchesBegan(touches: Set<UITouch>, 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<UITouch>, 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<UITouch>, 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 rearrange the cells without using the knobs. It can be expanded for different gestures or modes depending on the use case, but I left it as a vanilla version. Let me know if you have any questions.
Update:
Some reservations that I found. It is very important to find out how you want to use scrolling and change it accordingly. For example, when I mocked this, I set scrollingEnabled = false. . In the end, I wanted to scroll, so I changed the code to use this function only when pressed for longer than 0.25 (I used some timers for this). Then I temporarily turned off scrolling during this period and turned it back on after completion.
Unome
source share