Swift中UITableView单单元格选择及预选中实现问题
解决方案:实现UITableView每组单选+默认选中首项
你的问题核心在于没有跟踪每个分组的选中状态,而是依赖单元格自身的isSelected属性,这会导致cell复用混乱和同组多选的问题。下面是一步步的修复方案:
1. 添加状态变量记录每组选中索引
首先在FiltersVC类里添加一个字典,用来存储每个分组当前选中的行索引,初始值设置为每个组的第0行(默认选中首项):
class FiltersVC: UIViewController { // 其他已有变量... var selectedIndexes: [Int: Int] = [0: 0, 1: 0] // key: section索引,value: row索引 // ... }
2. 修改cellForRowAt,用数据源驱动选中状态
原来的代码依赖cell.isSelected来设置勾选标记,这在cell复用的时候会出错。现在改用我们的selectedIndexes来控制每个cell的状态:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = filterTableView.dequeueReusableCell(withIdentifier: "filterCell", for: indexPath) as! FiltersTableViewCell cell.tilteLbl.text = menuItems[indexPath.section][indexPath.row] cell.selectionStyle = .none cell.backgroundColor = UIColor.clear // 根据存储的选中索引判断是否显示勾选 let isCurrentSelected = selectedIndexes[indexPath.section] == indexPath.row cell.accessoryType = isCurrentSelected ? .checkmark : .none return cell }
3. 重写didSelectRowAt,实现同组单选逻辑
当用户选中某行时,更新对应分组的选中索引,然后刷新整个分组的单元格,取消其他行的勾选标记:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { // 更新当前分组的选中索引 selectedIndexes[indexPath.section] = indexPath.row // 保存选中项到UserDefaults let selectedItem = menuItems[indexPath.section][indexPath.row] let userDefaultsKey = indexPath.section == 0 ? "listBy" : "Cuisine" UserDefaults.standard.set(selectedItem, forKey: userDefaultsKey) UserDefaults.standard.synchronize() // 刷新当前分组,更新所有单元格的勾选状态 tableView.reloadSections(IndexSet(integer: indexPath.section), with: .automatic) // 更新按钮状态(保留你的原有逻辑) filterBtn.isUserInteractionEnabled = true filterBtn.backgroundColor = #colorLiteral(red: 0.9529120326, green: 0.3879342079, blue: 0.09117665142, alpha: 1) }
4. 移除didDeselectRowAt方法
因为我们的需求是每组必须有一个选中项,不需要处理取消选中的逻辑,所以直接删掉这个方法即可。
5. 初始化时加载默认选中状态
在viewDidLoad里调用reloadData,确保默认选中状态生效,同时可以手动触发一次选中逻辑,把默认值保存到UserDefaults:
override func viewDidLoad() { super.viewDidLoad() // 其他初始化代码(比如注册cell、设置delegate/dataSource)... // 加载默认选中状态 filterTableView.reloadData() // 手动触发默认选中的保存逻辑 let defaultSection0Index = IndexPath(row: 0, section: 0) tableView(filterTableView, didSelectRowAt: defaultSection0Index) let defaultSection1Index = IndexPath(row: 0, section: 1) tableView(filterTableView, didSelectRowAt: defaultSection1Index) }
为什么这样修复?
- 状态集中管理:用
selectedIndexes统一记录每组的选中状态,避免cell复用带来的状态混乱; - 数据源驱动UI:所有单元格的选中状态都由数据源控制,保证UI和数据一致;
- 同组单选逻辑:选中新行时刷新整个分组,自动取消同组其他行的勾选标记;
- 默认选中生效:初始化时通过设置默认索引和触发选中逻辑,确保首项默认选中且值保存到UserDefaults。
内容的提问来源于stack exchange,提问作者raheem
相关产品推荐
相关产品推荐

