使用新 iOS 11 API 重新排序 tableview 时动画不正确

Incorrect animation when reordering tableview with new iOS 11 APIs

我在 iOS 11 上使用新的拖放 API 对同一应用程序内的表格视图中的单元格重新排序。

这是我对 UITableViewDragDelegateUITableViewDropDelegate 的实现:

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)
    }
}

这工作正常,但有一个奇怪的故障。当最后一个单元格被拖动时,一旦它被放下,它会在表格视图的底部出现一瞬间然后消失。

我在这里错过了什么?

首先为swift4添加pressGesture这个例子

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
}

我希望它会起作用:)

只需要将table删除动画改为.automatic,像这样:

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

以后就不会出现那个奇怪的动画了