'Drag & Drop Table view cell has weired buggy animation while dragging cell (Using long press gesture)
I'm following this to perform drag and drop table view cell but I don't know why I have buggy weird animation while dragging cell to other position in the table. I don't know what actually causes this type of issue, any help or suggestion would be appreciated. You can check the code and result below:
Code:
var longGesture: UILongPressGestureRecognizer!
var scrollRate: CGFloat = 0
var dragInitialIndexPath: IndexPath?
var dragCellSnapshot: UIView?
var scrollDisplayLink: CADisplayLink?
self.longGesture = UILongPressGestureRecognizer(target: self, action: #selector(onLongPressGesture(sender:)))
self.longGesture.minimumPressDuration = 0.3
self.tblEditProject.addGestureRecognizer(self.longGesture)
//MARK: tableCell reorder / long press
@objc func onLongPressGesture(sender: UILongPressGestureRecognizer) {
self.longGesture = sender
let state = sender.state
let locationInView = self.longGesture.location(in: self.tblEditProject)
let indexPath = self.tblEditProject.indexPathForRow(at: locationInView)
switch state {
case .began:
if indexPath != nil {
guard let currentIndexPath = indexPath else { return }
dragInitialIndexPath = currentIndexPath
self.scrollDisplayLink = CADisplayLink(target: self, selector: #selector(scrollTableWithCell))
self.scrollDisplayLink?.add(to: .main, forMode: .default)
guard let cell = self.tblEditProject.cellForRow(at: currentIndexPath) else { return }
dragCellSnapshot = self.snapshotOfCell(inputView: cell)
var center = cell.center
dragCellSnapshot?.center = center
dragCellSnapshot?.alpha = 0.0
self.tblEditProject.addSubview(dragCellSnapshot!)
UIView.animate(withDuration: 0.25, animations: { () -> Void in
center.y = locationInView.y
self.dragCellSnapshot?.center = center
self.dragCellSnapshot?.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
self.dragCellSnapshot?.alpha = 0.98
cell.alpha = 0.0
}, completion: { _ in
cell.isHidden = true
})
}
case .changed:
if indexPath != nil {
self.calculateScroll(gestureRecognizer: self.longGesture)
var center = dragCellSnapshot?.center
center?.y = locationInView.y
dragCellSnapshot?.center = center!
if indexPath != nil && indexPath != dragInitialIndexPath {
// update your data model
let dataToMove = self.arrEditProject[0].section[dragInitialIndexPath!.row]
self.arrEditProject[0].section.remove(at: dragInitialIndexPath!.row)
self.arrEditProject[0].section.insert(dataToMove, at: indexPath!.row)
self.tblEditProject.moveRow(at: dragInitialIndexPath!, to: indexPath!)
dragInitialIndexPath = indexPath
}
}
case .ended:
guard let persistedIndexPath = dragInitialIndexPath else { return }
guard let cell = self.tblEditProject.cellForRow(at: persistedIndexPath) else { return }
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: { _ in
self.dragInitialIndexPath = nil
self.dragCellSnapshot?.removeFromSuperview()
self.dragCellSnapshot = nil
self.isUpdateContent = true
self.tblEditProject.reloadData()
/// For scrolling while dragging
self.scrollDisplayLink?.invalidate()
self.scrollDisplayLink = nil
self.scrollRate = 0
})
default:
guard let persistedIndexPath = dragInitialIndexPath else { return }
guard let cell = self.tblEditProject.cellForRow(at: persistedIndexPath) else { return }
cell.isHidden = false
cell.alpha = 0.0
UIView.animate(withDuration: 0.25) {
self.dragCellSnapshot?.center = cell.center
self.dragCellSnapshot?.transform = CGAffineTransform.identity
self.dragCellSnapshot?.alpha = 0.0
cell.alpha = 1.0
} completion: { _ in
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
}
func calculateScroll(gestureRecognizer: UIGestureRecognizer) {
print("Call scroll table with cell")
let location = gestureRecognizer.location(in: self.tblEditProject)
var rect: CGRect = self.tblEditProject.bounds
/// adjust rect for content inset as we will use it below for calculating scroll zones
rect.size.height -= self.tblEditProject.contentInset.top
/// tell us if we should scroll and which direction
let scrollZoneHeight = rect.size.height / 6
let bottomScrollBeginning = self.tblEditProject.contentOffset.y + self.tblEditProject.contentInset.top + rect.size.height - scrollZoneHeight
let topScrollBeginning = self.tblEditProject.contentOffset.y + self.tblEditProject.contentInset.top + scrollZoneHeight
/// we're in the bottom zone
if (location.y >= bottomScrollBeginning)
{
self.scrollRate = (location.y - bottomScrollBeginning) / scrollZoneHeight
}
/// we're in the top zone
else if (location.y <= topScrollBeginning)
{
self.scrollRate = (location.y - topScrollBeginning) / scrollZoneHeight
}
else
{
self.scrollRate = 0
}
}
@objc func scrollTableWithCell() {
print("Call scroll table with cell")
let gestureLong = self.longGesture
let location = gestureLong?.location(in: self.tblEditProject)
let currentOffset = self.tblEditProject.contentOffset
var newOffset = CGPoint(x: currentOffset.x, y: currentOffset.y + self.scrollRate * 10)
if (newOffset.y < -self.tblEditProject.contentInset.top) {
newOffset.y = -self.tblEditProject.contentInset.top
} else if (self.tblEditProject.contentSize.height + self.tblEditProject.contentInset.bottom < self.tblEditProject.frame.size.height) {
newOffset = currentOffset
} else if (newOffset.y > (self.tblEditProject.contentSize.height + self.tblEditProject.contentInset.bottom) - self.tblEditProject.frame.size.height) {
newOffset.y = (self.tblEditProject.contentSize.height + self.tblEditProject.contentInset.bottom) - self.tblEditProject.frame.size.height;
}
self.tblEditProject.setContentOffset(newOffset, animated: false)
if let lction = location {
if (lction.y >= 0 && lction.y <= self.tblEditProject.contentSize.height + 50) {
var center = dragCellSnapshot?.center
center?.y = lction.y
dragCellSnapshot?.center = center ?? CGPoint.zero
var indexPath = self.tblEditProject.indexPathForRow(at: lction)
/// Check if the pointer is bigger than the table height to set indexPath as the last cell
if (self.tblEditProject.contentSize.height < lction.y) {
indexPath = IndexPath(row: (self.tblEditProject.numberOfRows(inSection: 0)) - 1, section: 0)
}
if let pathIndex = indexPath {
if !pathIndex.isEmpty && pathIndex != dragInitialIndexPath {
// update your data model
let dataToMove = self.arrEditProject[0].section[dragInitialIndexPath!.row]
self.arrEditProject[0].section.remove(at: dragInitialIndexPath!.row)
self.arrEditProject[0].section.insert(dataToMove, at: pathIndex.row)
self.tblEditProject.moveRow(at: dragInitialIndexPath!, to: pathIndex)
dragInitialIndexPath = pathIndex
}
}
}
}
}
Result:
Solution 1:[1]
Apple's already performed Drag&Drop mechanism for UITableView and UICollectionView (Apple Documentation, How to use it). You don't need to develop it from scratch.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Shlykov Danylo |

