Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using long press gesture to reorder cells in tableview?

I want to be able to reorder tableview cells using a longPress gesture (not with the standard reorder controls). After the longPress is recognized I want the tableView to essentially enter 'edit mode' and then reorder as if I was using the reorder controls supplied by Apple.

Is there a way to do this without needing to rely on 3rd party solutions?

Thanks in advance.

EDIT: I ended up using the solution that was in the accepted answer and relied on a 3rd party solution.

like image 454
Michael Campsall Avatar asked Sep 04 '12 04:09

Michael Campsall


4 Answers

They added a way in iOS 11.

First, enable drag interaction and set the drag and drop delegates.

Then implement moveRowAt as if you are moving the cell normally with the reorder control.

Then implement the drag / drop delegates as shown below.

tableView.dragInteractionEnabled = true
tableView.dragDelegate = self
tableView.dropDelegate = self

func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { }

extension TableView: UITableViewDragDelegate {
func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
        return [UIDragItem(itemProvider: NSItemProvider())]
    }
} 

extension TableView: UITableViewDropDelegate {
    func tableView(_ tableView: UITableView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UITableViewDropProposal {

        if session.localDragSession != nil { // Drag originated from the same app.
            return UITableViewDropProposal(operation: .move, intent: .insertAtDestinationIndexPath)
        }

        return UITableViewDropProposal(operation: .cancel, intent: .unspecified)
    }

    func tableView(_ tableView: UITableView, performDropWith coordinator: UITableViewDropCoordinator) {
    }
}
like image 84
Grant Kamin Avatar answered Oct 13 '22 20:10

Grant Kamin


Swift 3 and no third party solutions

First, add these two variables to your class:

var dragInitialIndexPath: IndexPath?
var dragCellSnapshot: UIView?

Then add UILongPressGestureRecognizer to your tableView:

let longPress = UILongPressGestureRecognizer(target: self, action: #selector(onLongPressGesture(sender:)))
longPress.minimumPressDuration = 0.2 // optional
tableView.addGestureRecognizer(longPress)

Handle UILongPressGestureRecognizer:

// MARK: cell reorder / long press

func onLongPressGesture(sender: UILongPressGestureRecognizer) {
  let locationInView = sender.location(in: tableView)
  let indexPath = tableView.indexPathForRow(at: locationInView)

  if sender.state == .began {
    if indexPath != nil {
      dragInitialIndexPath = indexPath
      let cell = tableView.cellForRow(at: indexPath!)
      dragCellSnapshot = snapshotOfCell(inputView: cell!)
      var center = cell?.center
      dragCellSnapshot?.center = center!
      dragCellSnapshot?.alpha = 0.0
      tableView.addSubview(dragCellSnapshot!)

      UIView.animate(withDuration: 0.25, animations: { () -> Void in
        center?.y = locationInView.y
        self.dragCellSnapshot?.center = center!
        self.dragCellSnapshot?.transform = (self.dragCellSnapshot?.transform.scaledBy(x: 1.05, y: 1.05))!
        self.dragCellSnapshot?.alpha = 0.99
        cell?.alpha = 0.0
      }, completion: { (finished) -> Void in
        if finished {
          cell?.isHidden = true
        }
      })
    }
  } else if sender.state == .changed && dragInitialIndexPath != nil {
    var center = dragCellSnapshot?.center
    center?.y = locationInView.y
    dragCellSnapshot?.center = center!

    // to lock dragging to same section add: "&& indexPath?.section == dragInitialIndexPath?.section" to the if below
    if indexPath != nil && indexPath != dragInitialIndexPath {
      // update your data model
      let dataToMove = data[dragInitialIndexPath!.row]
      data.remove(at: dragInitialIndexPath!.row)
      data.insert(dataToMove, at: indexPath!.row)

      tableView.moveRow(at: dragInitialIndexPath!, to: indexPath!)
      dragInitialIndexPath = indexPath
    }
  } else if sender.state == .ended && dragInitialIndexPath != nil {
    let cell = tableView.cellForRow(at: dragInitialIndexPath!)
    cell?.isHidden = false
    cell?.alpha = 0.0
    UIView.animate(withDuration: 0.25, animations: { () -> Void in
      self.dragCellSnapshot?.center = (cell?.center)!
      self.dragCellSnapshot?.transform = CGAffineTransform.identity
      self.dragCellSnapshot?.alpha = 0.0
      cell?.alpha = 1.0
    }, completion: { (finished) -> Void in
      if finished {
        self.dragInitialIndexPath = nil
        self.dragCellSnapshot?.removeFromSuperview()
        self.dragCellSnapshot = nil
      }
    })
  }
}

func snapshotOfCell(inputView: UIView) -> UIView {
  UIGraphicsBeginImageContextWithOptions(inputView.bounds.size, false, 0.0)
  inputView.layer.render(in: UIGraphicsGetCurrentContext()!)
  let image = UIGraphicsGetImageFromCurrentImageContext()
  UIGraphicsEndImageContext()

  let cellSnapshot = UIImageView(image: image)
  cellSnapshot.layer.masksToBounds = false
  cellSnapshot.layer.cornerRadius = 0.0
  cellSnapshot.layer.shadowOffset = CGSize(width: -5.0, height: 0.0)
  cellSnapshot.layer.shadowRadius = 5.0
  cellSnapshot.layer.shadowOpacity = 0.4
  return cellSnapshot
}
like image 43
budiDino Avatar answered Oct 13 '22 21:10

budiDino


You can't do it with the iOS SDK tools unless you want to throw together your own UITableView + Controller from scratch which requires a decent amount of work. You mentioned not relying on 3rd party solutions but my custom UITableView class can handle this nicely. Feel free to check it out:

https://github.com/bvogelzang/BVReorderTableView

like image 6
bvogelzang Avatar answered Oct 13 '22 22:10

bvogelzang


So essentially you want the "Clear"-like row reordering right? (around 0:15)

This SO post might help.

Unfortunately I don't think you can do it with the present iOS SDK tools short of hacking together a UITableView + Controller from scratch (you'd need to create each row itself and have a UITouch respond relevant to the CGRect of your row-to-move).

It'd be pretty complicated since you need to get the animation of the rows "getting out of the way" as you move the row-to-be-reordered around.

The cocoas tool looks promising though, at least go take a look at the source.

like image 2
samuelsaumanchan Avatar answered Oct 13 '22 21:10

samuelsaumanchan