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: