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

TableView单元格开关互斥功能:for循环实现异常求助

问题分析

你当前代码的核心问题是直接通过tableView.cellForRow(at:)操作单元格——TableView采用复用机制,当单元格数量超过屏幕可见数量(比如4个)时,滚出屏幕的单元格会被回收,此时cellForRow根本无法获取到这些不在视图层级内的单元格,自然没法修改它们的开关状态。

正确解决方案:用数据模型管理状态

不要直接操作UI组件,应该通过数据层记录每个单元格的开关状态,再通过刷新UI同步状态,这是TableView开发的标准做法。

1. 定义状态存储变量

在你的ViewController中添加一个变量,用于记录每个section内当前选中的行:

// 字典结构:key为section索引,value为当前选中的row索引(nil表示无选中)
var selectedPolicyHolderRows: [Int: Int?] = [:]

2. 修改开关点击事件逻辑

func switchButtonStateChanged(isOn: Bool, indexPath: IndexPath) {
    // 保留你原有的webView交互逻辑
    if pravnoLice == 0 && baby == false {
        cellRow = indexPath[0] > 0 ? indexPath[1] + tableView.numberOfRows(inSection: 0) : indexPath[1]
    } else {
        cellRow = indexPath[0] > 1 ? indexPath[1] + tableView.numberOfRows(inSection: 1) : indexPath[1]
    }
    
    if isOn {
        webView.evaluateJavaScript("document.getElementById('Insurees_\(cellRow)__IsPolicyHolder').click();")
        
        // 更新数据模型:当前section仅保留当前行的选中状态
        selectedPolicyHolderRows[indexPath.section] = indexPath.row
        // 刷新整个section,让所有单元格根据数据模型同步状态
        tableView.reloadSections(IndexSet(integer: indexPath.section), with: .automatic)
    } else {
        webView.evaluateJavaScript("document.getElementById('Insurees_\(cellRow)__IsPolicyHolder').click();")
        // 关闭开关时,清空该section的选中记录
        selectedPolicyHolderRows[indexPath.section] = nil
        tableView.reloadRows(at: [indexPath], with: .automatic)
    }
}

3. 在cell创建/复用时绑定状态

确保cellForRowAt方法中,根据数据模型设置开关状态:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "InfoTableViewCell", for: indexPath) as! InfoTableViewCell
    
    // 根据数据模型设置开关状态
    if let selectedRow = selectedPolicyHolderRows[indexPath.section], selectedRow == indexPath.row {
        cell.policyHolderSwitch.isOn = true
    } else {
        cell.policyHolderSwitch.isOn = false
    }
    
    // 绑定开关点击事件(这里用闭包示例,你也可以用代理/tag,只要能传递indexPath即可)
    cell.policyHolderSwitch.addTarget(self, action: #selector(switchToggled(_:)), for: .valueChanged)
    cell.policyHolderSwitch.tag = indexPath.row
    // 或者用闭包更安全:
    // cell.onSwitchToggle = { [weak self] isOn in
    //     self?.switchButtonStateChanged(isOn: isOn, indexPath: indexPath)
    // }
    
    return cell
}

// 如果你用tag传递indexPath,需要补充这个方法
@objc func switchToggled(_ sender: UISwitch) {
    guard let cell = sender.superview?.superview as? InfoTableViewCell,
          let indexPath = tableView.indexPath(for: cell) else { return }
    switchButtonStateChanged(isOn: sender.isOn, indexPath: indexPath)
}

为什么这能解决问题?

  • 所有状态都存在数据模型中,不受单元格复用影响,即使单元格滚出屏幕,状态也不会丢失
  • 刷新section时,TableView会重新调用cellForRowAt,复用的单元格也能根据最新数据设置正确的开关状态
  • 彻底避免了直接操作单元格带来的复用bug

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 04:43:23