Swift中UITableView didSelectRowAt选中行错误问题排查与修复
问题原因与修复方案
核心问题
你的问题根源在于UITableViewCell的复用机制,以及错误地将选中状态存储在cell实例中,而非在数据源层面持久化记录选中状态:
- 表格滚动时cell会被复用,之前存在cell里的
isChecked状态会被带到新行,导致显示错位 didSelectRowAt里直接操作cell选中状态后调用reloadData,会清空所有cell的临时状态——因为reload后cell会重新创建/复用,但没有从持久化状态读取正确的选中标记
修复步骤
1. 在ViewController中添加持久化选中状态的变量
在StudentSignupStep4ViewController类中新增变量,专门记录当前选中的行索引:
class StudentSignupStep4ViewController: UIViewController { // 保留原有变量... var selectedRowIndex: Int? // 新增:持久化存储选中行的索引 }
2. 修改cellForRowAt方法,同步cell的选中状态
每次创建或复用cell时,根据selectedRowIndex设置cell的isChecked状态,确保cell显示与持久化状态一致:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: SubscriptionTableViewCell.cellId, for: indexPath) as! SubscriptionTableViewCell let cellData = subscriptionList?.result?.subscriptionData?[indexPath.row] cell.subscriptionNameLabel.text = cellData?.subscription_name // 新增:根据持久化状态设置cell的选中状态 cell.isChecked = indexPath.row == selectedRowIndex return cell }
3. 简化didSelectRowAt方法,更新状态并刷新表格
不需要遍历所有cell,只需更新selectedRowIndex后刷新表格,让cell读取最新的选中状态:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { selectedRowIndex = indexPath.row subsciptionId = subscriptionList?.result?.subscriptionData?[indexPath.row].id tableView.reloadData() // 可选优化:如果不想刷新整个表格,只刷新可见行更高效 // if let visibleRows = tableView.indexPathsForVisibleRows { // tableView.reloadRows(at: visibleRows, with: .automatic) // } }
4. (可选)补充cell复用重置逻辑
为了更严谨,在cell的prepareForReuse方法中重置状态,避免复用残留:
class SubscriptionTableViewCell: UITableViewCell { // 保留原有代码... override func prepareForReuse() { super.prepareForReuse() isChecked = false // 重置选中状态,等待cellForRowAt重新设置 } }
修复原理
- 将选中状态从临时的cell实例转移到ViewController的持久化变量中,避免复用导致的状态丢失
- 每次cell创建/复用时,从持久化状态读取并设置显示,彻底解决复用引发的错位问题
内容的提问来源于stack exchange,提问作者new dev
相关产品推荐
相关产品推荐

