You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)
}

为什么这样修复?

  1. 状态集中管理:用selectedIndexes统一记录每组的选中状态,避免cell复用带来的状态混乱;
  2. 数据源驱动UI:所有单元格的选中状态都由数据源控制,保证UI和数据一致;
  3. 同组单选逻辑:选中新行时刷新整个分组,自动取消同组其他行的勾选标记;
  4. 默认选中生效:初始化时通过设置默认索引和触发选中逻辑,确保首项默认选中且值保存到UserDefaults。

内容的提问来源于stack exchange,提问作者raheem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:21:25