Custom UITableView reorder

后端 未结 3 1999
粉色の甜心
粉色の甜心 2021-02-04 18:03

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

3条回答
  •  面向向阳花
    2021-02-04 18:27

    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.

提交回复
热议问题