Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reordering UITableView without reorder control

I need the user to be able to reorder a UITableView by this way: he touches a cell for a predetermined period (e.g. 1 second), then he can drag and drop it over the other cells.

I know how to implement the 'long touch' detection using a gesture recognizer, but what is the best way to implement the drag and drop ability without using a reorder control (the user should drag the cell from anywhere in the cell, not only from the reorder control)?

like image 665
Jo Roy Avatar asked Apr 11 '11 16:04

Jo Roy


4 Answers

This is an old question, but here's a solution that's tested and working with iOS 8 through 11.

In your UITableViewCell subclass try this:

class MyTableViewCell: UITableViewCell {
    weak var reorderControl: UIView?

    override func layoutSubviews() {
        super.layoutSubviews()

        // Make the cell's `contentView` as big as the entire cell.
        contentView.frame = bounds

        // Make the reorder control as big as the entire cell 
        // so you can drag from everywhere inside the cell.
        reorderControl?.frame = bounds
    }

    override func setEditing(_ editing: Bool, animated: Bool) {
        super.setEditing(editing, animated: false)
        if !editing || reorderControl != nil {
            return
        }

        // Find the reorder control in the cell's subviews.
        for view in subviews {
            let className = String(describing: type(of:view))
            if className == "UITableViewCellReorderControl" {

                // Remove its subviews so that they don't mess up
                // your own content's appearance.
                for subview in view.subviews {
                    subview.removeFromSuperview()
                }

                // Keep a weak reference to it for `layoutSubviews()`.
                reorderControl = view

                break
            }
        }
    }
}

It's close to Senseful's first suggestion but the article he references no longer seems to work.

What you do, is make the reorder control and the cell's content view as big as the whole cell when it's being edited. That way you can drag from anywhere within the cell and your content takes up the entire space, as if the cell was not being edited at all.

The most important downside to this, is that you are altering the system's cell view-structure and referencing a private class (UITableViewCellReorderControl). It seems to be working properly for all latest iOS versions, but you have to make sure it's still valid every time a new OS comes out.

like image 89
m_katsifarakis Avatar answered Oct 23 '22 06:10

m_katsifarakis


I solved the question of the following steps:

  1. Attach gesture recognizer to UITableView.
  2. Detect which cell was tapped by "long touch". At this moment create a snapshot of selected cell, put it to UIImageView and place it on the UITableView. UIImageView's coordinates should math selected cell relative to UITableView (snapshot of selected cell should overlay selected cell).
  3. Store index of selected cell, delete selected cell and reload UITableView.
  4. Disable scrolling for UITableView. Now you need to change frame of snapshot UIImageView when you will drag cell. You can do it in touchesMoved method.
  5. Create new cell and reload UITableView (you already have stored index) when the user finger leaves screen.
  6. Remove the snapshot UIImageView.

But it was not easy to do it.

like image 42
beryllium Avatar answered Oct 23 '22 07:10

beryllium


The article Reordering a UITableViewCell from any touch point discusses this exact scenario.

Essentially you do the following:

  1. Find the UITableViewCellReorderControl (a private class).
  2. Expand it so it spans the entire cell.
  3. Hide it.
  4. The user will now be able to drag the cell from anywhere.

Another solution, Cookbook: Moving Table View Cells with a Long Press Gesture, achieves the same effect by doing the following:

  1. Add a long press gesture recognizer on the table view.
  2. Create a snapshot of the cell when the cell is dragged.
  3. As the cell is dragged, move the snapshot around, and call the -[UITableView moveRowAtIndexPath:toIndexPath:].
  4. When the gesture ends, hide the cell snapshot.
like image 2
Senseful Avatar answered Oct 23 '22 07:10

Senseful


For future reference... I had the same problem, I found another question(Swift - Drag And Drop TableViewCell with Long Gesture Recognizer) about it and someone suggested this tutorial: https://www.freshconsulting.com/create-drag-and-drop-uitableview-swift/ worked just perfectly for me

like image 2
toiavalle Avatar answered Oct 23 '22 06:10

toiavalle