Incorrect animation when reordering a table view with new iOS 11 APIs

I am using the new drag and drop APIs on iOS 11 to reorder cells in a table view inside the same application.

Here is my implementation for UITableViewDragDelegateand UITableViewDropDelegate:

extension TasksViewController: UITableViewDragDelegate {
    func tableView(_ tableView: UITableView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
        let string = tasks[indexPath.row]
        guard let data = string.data(using: .utf8) else { return [] }
        let itemProvider = NSItemProvider(item: data as NSData, typeIdentifier: kUTTypePlainText as String)
        let item = UIDragItem(itemProvider: itemProvider)
        item.localObject = string

        return [item]
    }
}

extension TasksViewController: UITableViewDropDelegate {
    func tableView(_ tableView: UITableView, canHandle session: UIDropSession) -> Bool {
        return true
    }

    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) {
        guard let destinationIndexPath = coordinator.destinationIndexPath,
            let dragItem = coordinator.items.first?.dragItem,
            let task = dragItem.localObject as? String,
            let sourceIndexPath = coordinator.items.first?.sourceIndexPath else {
                return
        }

        tableView.performBatchUpdates({
            self.tasks.remove(at: sourceIndexPath.row)
            self.tasks.insert(task, at: destinationIndexPath.row)

            tableView.deleteRows(at: [sourceIndexPath], with: .none)
            tableView.insertRows(at: [destinationIndexPath], with: .none)
        })

        coordinator.drop(dragItem, toRowAt: destinationIndexPath)
    }
}

This works great, but has a weird glitch. When the last cell is dragged, as soon as it is discarded, it appears at the bottom of the table for the second second and then disappears.

enter image description here

What am I missing here?

+4
source share
2 answers

Just change the table deletion animation to .automatic, for example:

tableView.deleteRows(at: [sourceIndexPath], with: .automatic)

After that there will be no such strange animation.

+4
source

pressGesture 4

let longpress = UILongPressGestureRecognizer(target: self, action: #selector(longPressGestureRecognized(gestureRecognizer:)))
        tableView.addGestureRecognizer(longpress)

:

@objc func longPressGestureRecognized(gestureRecognizer: UIGestureRecognizer) {
        let longPress = gestureRecognizer as! UILongPressGestureRecognizer
        let state = longPress.state
        let locationInView = longPress.location(in: tableView)
        let indexPath = tableView.indexPathForRow(at: locationInView)
        struct My {
            static var cellSnapshot : UIView? = nil
            static var cellIsAnimating : Bool = false
            static var cellNeedToShow : Bool = false
        }
        switch state {
            case UIGestureRecognizerState.began:
                if indexPath != nil {
                    self.Move = indexPath
                    var cell:UITableViewCell? = UITableViewCell()
                    cell = tableView.cellForRow(at: indexPath!)

                    My.cellSnapshot  = snapshotOfCell(inputView: cell!)

                    var center = cell?.center
                    My.cellSnapshot!.center = center!
                    My.cellSnapshot!.alpha = 0.0
                    tableView.addSubview(My.cellSnapshot!)

                    UIView.animate(withDuration: 0.25, animations: { () -> Void in
                        center?.y = locationInView.y
                        My.cellIsAnimating = true
                        My.cellSnapshot!.center = center!
                        My.cellSnapshot!.transform = CGAffineTransform(scaleX: 1.05, y: 1.05)
                        My.cellSnapshot!.alpha = 0.98
                        cell?.alpha = 0.0
                    }, completion: { (finished) -> Void in
                        if finished {
                            My.cellIsAnimating = false
                            if My.cellNeedToShow {
                                My.cellNeedToShow = false
                                UIView.animate(withDuration: 0.25, animations: { () -> Void in
                                    cell?.alpha = 1
                                })
                            } else {
                                cell?.isHidden = true
                            }
                        }
                    })
                }
                break;
            case UIGestureRecognizerState.changed:
                if My.cellSnapshot != nil {
                    var center = My.cellSnapshot!.center
                    center.y = locationInView.y
                    My.cellSnapshot!.center = center

                    if ((indexPath != nil) && (indexPath != Move)) && Move != nil {
                        self.list.insert(self.list.remove(at: Move!.row), at: indexPath!.row) //this line change row index
                        tableView.moveRow(at: Move!, to: indexPath!)
                        Move = indexPath
                    }
                }
                break;
            default:
                if Move != nil {
                    let cell = tableView.cellForRow(at: Move!)
                    if My.cellIsAnimating {
                        My.cellNeedToShow = true
                    } else {
                        cell?.isHidden = false
                        cell?.alpha = 0.0
                    }

                    UIView.animate(withDuration: 0.25, animations: { () -> Void in
                        My.cellSnapshot!.center = (cell?.center)!
                        My.cellSnapshot!.transform = CGAffineTransform.identity
                        My.cellSnapshot!.alpha = 0.0
                        cell?.alpha = 1.0

                    }, completion: { (finished) -> Void in
                        if finished {
                            self.Move = nil
                            My.cellSnapshot!.removeFromSuperview()
                            My.cellSnapshot = nil
                        }
                    })
                }
                break;
        }
    }

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

    let cellSnapshot : UIView = 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
}

, :)

-2

Source: https://habr.com/ru/post/1687028/


All Articles