如何在每次出现新行时更新所有可见的 UITableViewCell
How To Update All The Visible UITableViewCell Every Time A New Row Appears
我希望 UITableView
的刷新由新单元格的出现触发。
设置简单。 UITableView
、dataSource
为 [Double]
。 table 中的每一行将在数组中显示一个数字。
我想做的特别的事情是我想要table用所有可见单元格的最大数量标记单元格。然后每个其他单元格将计算与屏幕上最大数字的差异。每次出现新单元格时都应该更新。
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[[=10=].row] }
.reduce(0) { Swift.max([=10=],) }
// 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 个才能适合屏幕,但在 table 加载阶段,模拟器认为在某些时候有 19 个可见单元格
如果相反,我取消注释 tableView.reloadData()
,这行代码将触发 willDisplay
func,它将再次调用 calculate
func,其中包含 reloadData()
。这是一个无限递归循环,没有一行成功显示在屏幕上。
相反,如果我不取消注释任何内容,table 将不会更新屏幕上已有的单元格。只有新出现的单元格才能正确显示我想要的效果。
感谢您阅读所有这些内容并尝试提供帮助。
您不想调用 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
,您将在控制台中收到一条消息:
Attempted to call -cellForRowAtIndexPath: on the table view while it was in the process of updating its visible cells, which is not allowed.
这会导致发布版本崩溃。
为了解决这个问题,我们可以通过异步调度来推迟对 updateVisibleCells
的调用,以便在 table 视图更新完成后调用它:
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
self.calculate()
DispatchQueue.main.async {
self.updateVisibleCells()
}
}
您想使用 didEndDisplaying
而不是 willDisplay
以便在行滚出视图时正确更新行。您不需要 willDisplay
中的任何内容
我希望 UITableView
的刷新由新单元格的出现触发。
设置简单。 UITableView
、dataSource
为 [Double]
。 table 中的每一行将在数组中显示一个数字。
我想做的特别的事情是我想要table用所有可见单元格的最大数量标记单元格。然后每个其他单元格将计算与屏幕上最大数字的差异。每次出现新单元格时都应该更新。
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[[=10=].row] }
.reduce(0) { Swift.max([=10=],) }
// 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 个才能适合屏幕,但在 table 加载阶段,模拟器认为在某些时候有 19 个可见单元格
如果相反,我取消注释 tableView.reloadData()
,这行代码将触发 willDisplay
func,它将再次调用 calculate
func,其中包含 reloadData()
。这是一个无限递归循环,没有一行成功显示在屏幕上。
相反,如果我不取消注释任何内容,table 将不会更新屏幕上已有的单元格。只有新出现的单元格才能正确显示我想要的效果。
感谢您阅读所有这些内容并尝试提供帮助。
您不想调用 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
,您将在控制台中收到一条消息:
Attempted to call -cellForRowAtIndexPath: on the table view while it was in the process of updating its visible cells, which is not allowed.
这会导致发布版本崩溃。
为了解决这个问题,我们可以通过异步调度来推迟对 updateVisibleCells
的调用,以便在 table 视图更新完成后调用它:
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
self.calculate()
DispatchQueue.main.async {
self.updateVisibleCells()
}
}
您想使用 didEndDisplaying
而不是 willDisplay
以便在行滚出视图时正确更新行。您不需要 willDisplay