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

Posted

技术标签:

【中文标题】使用新的 iOS 11 API 重新排序 tableview 时动画不正确【英文标题】:Incorrect animation when reordering tableview with new iOS 11 APIs 【发布时间】:2017-10-06 12:25:26 【问题描述】:

我正在使用 ios 11 上的新拖放 API 对同一应用内的 tableview 中的单元格重新排序。

这是我对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)
    

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

我在这里错过了什么?

【问题讨论】:

只需将插入和删除动画更改为自动而不是无!就是这样! 【参考方案1】:

只需将表格删除动画更改为 .automatic,如下所示:

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

之后就不会有那个奇怪的动画了。

【讨论】:

我虽然已经尝试过,但显然我没有。现在可以使用了,谢谢!【参考方案2】:

首先为swift 4添加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

我希望它会工作:)

【讨论】:

无需长按手势!这是完全不同的事情。 OP 要求其他的东西!

以上是关于使用新的 iOS 11 API 重新排序 tableview 时动画不正确的主要内容,如果未能解决你的问题,请参考以下文章

mysql 表单记录主键重新重1开始排序

什么是新的“iOS 数据保护 API”?

如何使用新的 iOS 7 API 获取自动换行信息?

插入后的NSMutableDictionary重新排序元素[重复]

在 Material-Table 中重新排序后是不是可以获得当前页面数据?

Vue中使用andt组件a-table列表数据根据点击的表头进行升/降排序-案例