UITableViewDelegate的didDeselectRowAt方法再次点击行时不触发的问题
实现UITableView单元格点击切换勾选状态的完整方案
我来帮你搞定这个单元格点击切换勾选的需求~你现在的问题主要是缺少状态记录和UI更新的逻辑,而且要注意UITableView的复用特性,直接操作cell的accessoryType会导致滚动后状态错乱,下面是完整的实现步骤:
1. 定义状态数组记录选中状态
首先需要一个数组来保存每个单元格的选中状态,因为UITableView的cell是复用的,不能只依赖cell自身的属性来保存状态:
// 假设你的数据源是存储单元格标题的数组 var cellTitles = ["测试项1", "测试项2", "测试项3", "测试项4"] // 对应每个单元格的选中状态,默认都为未选中 var selectedRows = [Bool]()
2. 初始化状态数组
在viewDidLoad里初始化状态数组,同时要开启表格的多选模式(否则再次点击单元格不会触发didDeselectRowAt方法):
override func viewDidLoad() { super.viewDidLoad() // 初始化选中状态数组,长度和数据源一致,默认全为false selectedRows = Array(repeating: false, count: cellTitles.count) // 开启多选模式,这样再次点击已选中的单元格才会触发取消选中的回调 tableView.allowsMultipleSelection = true }
3. 在cellForRowAt中根据状态设置UI
在创建/复用单元格的时候,根据状态数组的值来设置accessoryType,避免滚动后状态丢失:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { // 这里替换成你自己的cell标识符 let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) cell.textLabel?.text = cellTitles[indexPath.row] // 根据选中状态设置勾选标记 cell.accessoryType = selectedRows[indexPath.row] ? .checkmark : .none return cell }
4. 完善didSelect和didDeselect方法
在这两个回调里更新状态数组,然后刷新对应的单元格:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { print("Did select at indexPath \(indexPath)") // 标记该单元格为已选中 selectedRows[indexPath.row] = true // 刷新单元格UI tableView.reloadRows(at: [indexPath], with: .automatic) } func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) { print("Did deselect at indexPath \(indexPath)") // 标记该单元格为未选中 selectedRows[indexPath.row] = false // 刷新单元格UI tableView.reloadRows(at: [indexPath], with: .automatic) }
关键注意点
- 必须开启
allowsMultipleSelection = true:单选模式下,再次点击已选中的单元格只会触发didSelectRowAt,不会触发didDeselectRowAt,无法实现取消勾选的逻辑。 - 必须用状态数组记录选中状态:直接修改cell的
accessoryType后,当cell被复用时,新的单元格会继承旧的状态,导致滚动后勾选状态错乱。
内容的提问来源于stack exchange,提问作者Tigran Iskandaryan
相关产品推荐
相关产品推荐

