Using long press gesture to reorder cells in tableview?
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
}
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) {
}
}