【问题标题】:Why do animations not work when deselecting a UITableViewCell in a UITableView asynchronously?为什么异步取消选择 UITableView 中的 UITableViewCell 时动画不起作用?
【发布时间】:2021-11-08 14:11:18
【问题描述】:

为什么在异步取消选择UITableView 中的UITableViewCell 时动画不起作用?

下面是一个简单的例子。如果您点击并释放第一个单元格,它会按照您的预期取消选择动画。

如果您点击并释放第二个单元格,您可能根本不会注意到任何事情发生。您甚至可能需要按住单元格才能看到所选内容,当您松开单元格时,它会立即恢复为未选中状态。

这显然是一个人为的例子,但它显示了问题的本质。如果我想推迟取消选择,直到发生某些事情,我将面临同样的问题。

为什么会发生这种情况,有什么办法可以解决吗?

import UIKit

class ViewController: UITableViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
    }
    
    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 2
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
        cell.textLabel?.text = indexPath.row == 0 ? "Regular deselection" : "Async deselection"
        return cell
    }
    
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if indexPath.row == 0 {
            tableView.deselectRow(at: indexPath, animated: true)
        } else {
            DispatchQueue.main.async {
                tableView.deselectRow(at: indexPath, animated: true)
            }
        }
    }
}

【问题讨论】:

    标签: ios swift uitableview asynchronous


    【解决方案1】:

    这有点好奇……

    有许多 UI 控件具有“内部流程”——也就是说,发生了一些我们没有立即意识到的事情。

    一个例子是标准的UIButton。在触摸/触摸序列期间,按钮的标题标签会从.normal 交叉淡入淡出到.highlighted 并再次返回。

    所以,我的假设是:

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        if indexPath.row == 0 {
            // 1
            tableView.deselectRow(at: indexPath, animated: true)
        } else {
            DispatchQueue.main.async {
                // 2
                tableView.deselectRow(at: indexPath, animated: true)
            }
        }
    }
    

    对于第一种情况,UIKit 可能会将高亮/取消高亮序列“排队”。

    而在第二种情况下,我们告诉表格视图在下一个运行循环中取消选择该行......这将立即发生。那时,我们(实际上)中断了默认顺序,并且表格视图在它完成之前(或者实际上,在它开始之前)突出显示该行之前取消突出显示该行。

    更奇怪的是...如果我们选择了一行,然后在稍后的时间点(例如在其他地方点击)并然后调用tableView.deselectRow(at: indexPath, animated: true),动画就会出现 要短得多,甚至看起来都没有动画效果。

    这里有一些可以玩的东西——也许你会找到一种适合你目标的方法。

    class DeselViewController: UITableViewController {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
            
            let noAnimBtn = UIBarButtonItem(title: "No Anim", style: .plain, target: self, action: #selector(noAnim(_:)))
            let animBtn = UIBarButtonItem(title: "With Anim", style: .plain, target: self, action: #selector(withAnim(_:)))
            navigationItem.rightBarButtonItems = [animBtn, noAnimBtn]
    
        }
    
        @objc func noAnim(_ b: Any?) -> Void {
            if let p = tableView.indexPathForSelectedRow {
                tableView.deselectRow(at: p, animated: false)
            }
        }
        
        @objc func withAnim(_ b: Any?) -> Void {
            if let p = tableView.indexPathForSelectedRow {
                UIView.animate(withDuration: 0.3, animations: {
                    self.tableView.deselectRow(at: p, animated: false)
                })
            }
        }
        
        override func numberOfSections(in tableView: UITableView) -> Int {
            return 1
        }
        
        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return 8
        }
        
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
            switch indexPath.row {
            case 0:
                cell.textLabel?.text = "Regular deselection"
            case 1:
                cell.textLabel?.text = "Async deselection"
            case 2:
                cell.textLabel?.text = "Async deselection with Anim Duration"
            case 3:
                cell.textLabel?.text = "Asnyc Delay deselection"
            case 4:
                cell.textLabel?.text = "Asnyc Delay Plus Anim Duration deselection"
            case 5:
                cell.textLabel?.text = "Asnyc Long Delay deselection"
            case 6:
                cell.textLabel?.text = "Asnyc Long Delay Plus Anim Duration deselection"
            default:
                cell.textLabel?.text = "Manual deselection"
            }
            return cell
        }
        
        override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
            switch indexPath.row {
            case 0:
                tableView.deselectRow(at: indexPath, animated: true)
            case 1:
                DispatchQueue.main.async {
                    tableView.deselectRow(at: indexPath, animated: true)
                }
            case 2:
                DispatchQueue.main.async {
                    UIView.animate(withDuration: 0.3, animations: {
                        tableView.deselectRow(at: indexPath, animated: true)
                    })
                }
            case 3:
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
                    tableView.deselectRow(at: indexPath, animated: true)
                })
            case 4:
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1, execute: {
                    UIView.animate(withDuration: 0.3, animations: {
                        tableView.deselectRow(at: indexPath, animated: true)
                    })
                })
            case 5:
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.75, execute: {
                    tableView.deselectRow(at: indexPath, animated: true)
                })
            case 6:
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.75, execute: {
                    UIView.animate(withDuration: 0.3, animations: {
                        tableView.deselectRow(at: indexPath, animated: true)
                    })
                })
            default:
                ()
                // leave selected
            }
        }
    }
    

    将其设置为导航控制器的根视图,以便它可以放置“手动”取消选择的右栏按钮:

    【讨论】:

    • 带有 UIView 动画的异步块似乎效果最好。但我对这个解决方案并不完全满意。这是一个折腾,只是不用担心丢失的动画。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-17
    • 2020-02-22
    • 1970-01-01
    • 2020-08-29
    • 1970-01-01
    相关资源
    最近更新 更多