每次出现新单元格时如何访问 UITableView.visibleCells

How To Access UITableView.visibleCells Every Time New Cell Emerge

我有一个显示 [Double] 的 tableView。很简单的。但我也想 显示屏幕上每行数字的平均值,以及这个数字与平均值之间的差异

因为每次出现新行都需要重新计算平均值,所以我想在cellForRowAt: indexPath方法中访问tableView.visibleCells,然后更新这一行的平均值和每屏幕上的其他行,因为屏幕上所有行的平均行数应该相同。

但是后来我收到了这条错误信息[Assert] Attempted to access the table view's visibleCells while they were in the process of being updated, which is not allowed. Make a symbolic breakpoint at UITableViewAlertForVisibleCellsAccessDuringUpdate to catch this in the debugger and see what caused this to occur. Perhaps you are trying to ask the table view for the visible cells from inside a table view callback about a specific row?

虽然这很清楚,但我想知道正确的方法或解决方法是什么?

代码很简单

class ViewController: UIViewController {
    
    var data:[Double] = [13,32,43,56,89,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()        
        self.tableView.dataSource = self
        self.tableView.delegate = self
        
    }

}

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])"
        print(tableView.visibleCells.count) // THIS LINE PRODUCE ERROR
        return cell    
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 64
    }
    
}

我试过的:

我已经尝试了didEndDisplayingwillDisplay,当我向其中任何一个添加print(tableView.visibleCells.count)时,返回相同的错误信息。

答案:

您可以使用 UITableView 的委托函数来计算。

tableView(_:willDisplay:forRowAt:) 每次在单元格可见之前调用,因此您可以在此时重新计算平均值。此外,还有 tableView(_:didEndDisplaying:forRowAt:) 当单元格关闭显示时触发,也可用于重新计算。


文档:

tableView(_:willDisplay:forRowAt:)

tableView(_:didEndDisplaying:forRowAt:)


更新:

计算用tableView.indexPathsForVisibleItems


示例:

import UIKit

class ViewController: UIViewController {
    
    var data:[Double] = [13,32,43,56,89,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() {
        let count = tableView.indexPathsForVisibleRows?.count
        let sum = tableView.indexPathsForVisibleRows?
            .map { data[[=10=].row] }
            .reduce(0) { [=10=] +  }
        
        if let count = count, let sum = sum {
            print(sum / Double(count))
        }
    }
}

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])"
        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()
    }
}