点击UITableView单元格Accessory按钮切换为对勾标记的实现问题
解决UITableView Accessory点击后切换类型的断言错误问题
嘿,我来帮你搞定这个问题~
首先,你遇到的断言失败错误,根源在于在accessoryButtonTappedForRowWith方法里调用了dequeueReusableCell。这个方法的作用是从复用池里取出一个可复用的单元格,而不是获取当前已经显示在列表中对应行的那个单元格,所以系统会抛出断言错误。
正确的实现步骤
要实现点击 accessory 按钮后切换为对勾,同时保证列表滚动时状态不会丢失,你需要做两件事:
- 用一个数组记录每行的「已点击/模拟下载完成」状态(避免cell复用导致状态重置)
- 点击按钮时,更新状态并直接获取当前显示的单元格修改UI
完整代码示例
首先在你的ViewController里添加一个状态数组:
// 用来记录每行是否已经被点击(模拟下载完成状态) var isRowDownloaded: [Bool] = [] override func viewDidLoad() { super.viewDidLoad() // 初始化状态数组,长度和你的TableData一致,默认都是未点击/未下载 isRowDownloaded = Array(repeating: false, count: TableData.count) }
然后修改cellForRowAt方法,根据状态设置accessory类型:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) cell.textLabel?.text = TableData[indexPath.row] // 根据状态设置对应的accessory类型 if isRowDownloaded[indexPath.row] { cell.accessoryType = .checkmark } else { cell.accessoryType = .detailDisclosureButton } return cell }
最后修改accessoryButtonTappedForRowWith方法:
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { // 1. 更新状态数组 isRowDownloaded[indexPath.row] = true // 2. 获取当前显示的单元格,直接修改accessory类型 if let cell = tableView.cellForRow(at: indexPath) { cell.accessoryType = .checkmark } // 如果以后要做真实的下载逻辑,这里可以替换成异步下载代码,下载完成后再执行状态更新和UI修改 // 比如: // downloadAudio(at: indexPath.row) { [weak self] success in // guard let self = self else { return } // if success { // self.isRowDownloaded[indexPath.row] = true // DispatchQueue.main.async { // if let cell = tableView.cellForRow(at: indexPath) { // cell.accessoryType = .checkmark // } // } // } // } }
为什么要这么做?
UITableView的单元格是复用机制,也就是说当你滚动列表时,离开屏幕的cell会被放进复用池,新进入屏幕的cell会从复用池里取出重新配置。如果只修改当前显示的cell的UI而不记录状态,滚动之后新复用的cell会回到初始的.detailDisclosureButton状态,导致对勾消失。所以必须用一个状态数组来持久化每行的状态,这样每次cell被复用的时候,都能根据状态正确设置accessory类型。
内容的提问来源于stack exchange,提问作者darkginger
相关产品推荐
相关产品推荐

