每次出现新行时如何更新所有可见的 UITableViewCell

Posted

技术标签:

【中文标题】每次出现新行时如何更新所有可见的 UITableViewCell【英文标题】:How To Update All The Visible UITableViewCell Every Time A New Row Appears 【发布时间】:2021-02-10 02:56:16 【问题描述】:

我希望UITableView 的刷新由新单元格的出现触发。

简单的设置。 UITableViewdataSource[Double]。表格中的每一行将在数组中显示一个数字。

我想做的特别的事情是我希望表格用所有可见单元格的最大数量标记单元格。然后每个其他单元格将计算与屏幕上的最大数量的差异。并且每次出现新单元格时都会更新。

import UIKit

class ViewController: UIViewController 
    var max = 0.0
    var data:[Double] = [13,32,43,56,91,42,26,17,63,41,73,54,26,87,64,33,26,51,99,85,57,43,30,33,20]
    
    @IBOutlet weak var tableView: UITableView!
    
    override func viewDidLoad() 
        super.viewDidLoad()
        view.backgroundColor = .red
        self.tableView.dataSource = self
        self.tableView.delegate = self
        self.tableView.reloadData()
    
    
    private func calculate() 
        // calculate the max of visible cells
        let indexes = tableView.indexPathsForVisibleRows!
        max = indexes
            .map  data[$0.row] 
            .reduce(0)  Swift.max($0,$1) 
        
        // trying to update all the visible cells.

//         tableView.reloadRows(at: indexes, with: .none) ****
//         tableView.reloadData() ****
        
    


extension ViewController: UITableViewDataSource, UITableViewDelegate 
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        data.count
    
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell  = tableView.dequeueReusableCell(withIdentifier: "default")!
        cell.textLabel?.text = "\(data[indexPath.row]) : \(max)"
        return cell
    
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat 
        return 64
    
    
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) 
        calculate()
    
    
    func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) 
        calculate()
    

我做了什么

有2行代码,末尾带有****标记。

如果我取消注释 tableView.reloadRows(at: indexes, with: .none),Xcode 会产生错误:'NSRangeException', reason: '*** -[__NSArrayM objectAtIndexedSubscript:]: index 15 beyond bounds [0 .. 13]'。我真的不明白为什么,但我知道屏幕上的可见单元格最多为 14 个以适合屏幕,但在表格加载阶段,模拟器认为在某些时候有 19 个可见单元格

如果相反,我取消注释tableView.reloadData(),这行代码将触发willDisplay func,它将再次调用calculate func,其中包含reloadData()。这是一个无限递归循环,屏幕上没有成功显示任何行。

如果我不取消任何注释,表格将不会更新屏幕上已经显示的单元格。只有新出现的单元格才能正确显示我想要的效果。

感谢您阅读所有内容并尝试提供帮助。

【问题讨论】:

您无法重新加载单元格,因为这将触发无限循环。在willDisplayCell 中,您可以使用cellForRow(at:) 获取当前屏幕上的单元格并直接对其进行更新。 @Paulw11,非常感谢您的回复。所以我删除了didEndDisplaying。我将calculate() 中的所有内容添加到willDisplay 中,并添加了额外的代码,该代码使用for 循环来查找所有使用indexes 的单元格,并将它们的textLabel.text 设置为新字符串和cell.setNeedsDisplay(),但似乎这样设置新文本的代码被忽略了。 【参考方案1】:

您不想调用reloadCells,因为这会触发willDisplay,然后您会陷入无限循环。

相反,您可以访问可见单元格并直接通过为可见单元格调用cellForRow(at:) 来更新它。

private func updateVisibleCells() 
    for indexPath in self.tableView.indexPathsForVisibleRows ?? [] 
        if let cell = self.tableView.cellForRow(at: indexPath) 
            cell.textLabel?.text = "\(data[indexPath.row]) : \(max)"
        
    

现在,如果您在willDisplayCell 中调用calculate() 之后直接调用updateVisibleCells,您将在控制台中收到一条消息:

在更新可见单元格的过程中尝试在表格视图上调用 -cellForRowAtIndexPath:,这是不允许的。

这将导致发布版本崩溃。

要解决这个问题,我们可以通过异步调度它来推迟对updateVisibleCells 的调用,以便在表格视图更新完成后调用它:

func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) 
    self.calculate()
    DispatchQueue.main.async 
        self.updateVisibleCells()
    

您想使用didEndDisplaying 而不是willDisplay,以便在行滚动出视图时正确更新行。 willDisplay中你不需要任何东西

【讨论】:

这段代码对我有用。我的整个视图控制器都在这里 - gist.github.com/paulw11/1d3f043e610f836d35927040b8258085 是的,重要的是不要混淆表格视图方法和数据源方法(你不应该自称) 再次感谢您的代码。但我遇到了另一个问题。我在您的代码中将data 更改为[100,99,98, ... 1],它工作正常,除非它滚动到底部。在底部,如果我滚动太多,超出边界,放手后,tableView 会反弹,但是当我滚动太多时,最大值只会反映屏幕上的那些行,但不会反映屏幕上的行在tableView 反弹之后。所以在我的情况下,我的屏幕可以占用 13 行,我向下滚动直到有 9 行,然后它反弹回来,最大值是 9,而不是屏幕上的 13。 我通过在willDisplay cell 中添加updateVisibleCells 解决了这个问题。 ty

以上是关于每次出现新行时如何更新所有可见的 UITableViewCell的主要内容,如果未能解决你的问题,请参考以下文章

具有不可见字符的新行不会换行吗?

Sql 触发器在表更新时添加新行

如何对儿童可见度的变化做出反应?

解析文件并在每次出现后插入新行

WPF DataGrid如何获取ItemsSource更新时

Wpf DataGrid添加新行