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

UITableView滚动时显示错误数据问题求助

解决UITableViewCell复用导致的状态错乱问题

这个问题的核心原因是UITableView的单元格复用机制:当你滚动列表时,已经滑出屏幕的cell会被系统回收,重新分配给新进入屏幕的行。如果你的cell状态(复选框选中状态、文本框内容)只存在cell实例本身,没有和数据源绑定,那么复用的时候就会带上之前的状态,导致错乱。

下面是具体的修复方案,分步骤来:

1. 完善数据源结构,存储每行的状态

首先,你需要给每个数据项(也就是resolutionDetailsArray里的字典)添加对应的状态字段,用来记录该行的复选框选中状态和文本框内容。比如:

// 初始化数据时,给每个字典补充状态字段
for item in resolutionDetailsArray {
    let dic = item as! NSMutableDictionary
    dic["isOptionSelected"] = false
    dic["isPlannedSelected"] = false
    dic["isDoneSelected"] = false
    dic["whoText"] = ""
}

2. 在cellForRowAt中根据数据源重置cell状态

每次复用cell的时候,必须从数据源读取对应行的状态,然后同步到cell的UI上,覆盖掉复用带来的旧状态。修改你的cellForRowAt方法:

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell: UpdatingListTableViewCell = self.standardsProceduresView.dequeueReusableCellWithIdentifier(identifier: "cellClass") as! UpdatingListTableViewCell
    cell.selectionStyle = .none
    cell.delegate = self
    
    // 从数据源获取当前行的数据和状态
    resolutionDic = resolutionDetailsArray[indexPath.row] as! NSMutableDictionary
    cell.stdProcedLbl.text = "\(resolutionDic["Resolution"]!)"
    
    // 配置optionBtn的状态与图片
    let isOptionSelected = resolutionDic["isOptionSelected"] as! Bool
    cell.optionBtn.isSelected = isOptionSelected
    let optionImage = isOptionSelected ? UIImage(named: "checkbox.png") : UIImage(named: "square.png")
    cell.optionBtn.setImage(optionImage, for: .normal)
    
    // 配置plannedBtn的状态、图片与启用状态
    let isPlannedSelected = resolutionDic["isPlannedSelected"] as! Bool
    cell.plannedBtn.isSelected = isPlannedSelected
    let plannedImage = isPlannedSelected ? UIImage(named: "checkbox_planned.png") : UIImage(named: "square.png")
    cell.plannedBtn.setImage(plannedImage, for: .normal)
    cell.plannedBtn.isEnabled = isOptionSelected
    
    // 配置doneBtn的状态、图片与启用状态
    let isDoneSelected = resolutionDic["isDoneSelected"] as! Bool
    cell.doneBtn.isSelected = isDoneSelected
    let doneImage = isDoneSelected ? UIImage(named: "checkbox_done.png") : UIImage(named: "square.png")
    cell.doneBtn.setImage(doneImage, for: .normal)
    cell.doneBtn.isEnabled = isOptionSelected
    
    // 配置文本框内容
    cell.whoTextField.text = resolutionDic["whoText"] as? String
    cell.whoTextField.tag = indexPath.row // 绑定行号,方便后续更新数据源
    
    return cell
}

3. 修改代理方法,更新数据源而非仅修改cell UI

原来的代理方法只修改了cell的UI,现在需要同步更新数据源里的状态,这样下次复用cell时才能正确读取:

func optionBtnTapped(cell: UpdatingListTableViewCell) {
    guard let indexPath = standardsProceduresView.tableview?.indexPath(for: cell) else { return }
    let dic = resolutionDetailsArray[indexPath.row] as! NSMutableDictionary
    
    // 切换选中状态并更新数据源
    let newState = !(dic["isOptionSelected"] as! Bool)
    dic["isOptionSelected"] = newState
    
    // 更新cell UI
    cell.optionBtn.isSelected = newState
    let image = newState ? UIImage(named: "checkbox.png") : UIImage(named: "square.png")
    cell.optionBtn.setImage(image, for: .normal)
    
    // 同步控制planned和done按钮的启用状态,取消option时可重置其选中状态(按需调整)
    cell.plannedBtn.isEnabled = newState
    cell.doneBtn.isEnabled = newState
    if !newState {
        dic["isPlannedSelected"] = false
        dic["isDoneSelected"] = false
        cell.plannedBtn.isSelected = false
        cell.doneBtn.isSelected = false
        cell.plannedBtn.setImage(UIImage(named: "square.png"), for: .normal)
        cell.doneBtn.setImage(UIImage(named: "square.png"), for: .normal)
    }
}

// plannedBtnTapped和doneBtnTapped方法同理
func plannedBtnTapped(cell: UpdatingListTableViewCell) {
    guard let indexPath = standardsProceduresView.tableview?.indexPath(for: cell) else { return }
    let dic = resolutionDetailsArray[indexPath.row] as! NSMutableDictionary
    
    let newState = !(dic["isPlannedSelected"] as! Bool)
    dic["isPlannedSelected"] = newState
    
    cell.plannedBtn.isSelected = newState
    let image = newState ? UIImage(named: "checkbox_planned.png") : UIImage(named: "square.png")
    cell.plannedBtn.setImage(image, for: .normal)
}

func doneBtnTapped(cell: UpdatingListTableViewCell) {
    guard let indexPath = standardsProceduresView.tableview?.indexPath(for: cell) else { return }
    let dic = resolutionDetailsArray[indexPath.row] as! NSMutableDictionary
    
    let newState = !(dic["isDoneSelected"] as! Bool)
    dic["isDoneSelected"] = newState
    
    cell.doneBtn.isSelected = newState
    let image = newState ? UIImage(named: "checkbox_done.png") : UIImage(named: "square.png")
    cell.doneBtn.setImage(image, for: .normal)
}

4. 处理文本框内容的保存

为了让文本框的内容在复用后不会丢失,需要在文本框编辑结束时把内容保存到数据源中:

// 先更新你的代理协议,添加文本变更方法
protocol updatingDelegate: AnyObject {
    func optionBtnTapped(cell: UpdatingListTableViewCell)
    func plannedBtnTapped(cell: UpdatingListTableViewCell)
    func doneBtnTapped(cell: UpdatingListTableViewCell)
    func whoTextChanged(row: Int, text: String) // 新增方法
}

// 在UpdatingListTableViewCell的文本框代理方法中传递内容
extension UpdatingListTableViewCell {
    func textFieldDidEndEditing(_ textField: UITextField) {
        delegate?.whoTextChanged(row: textField.tag, text: textField.text ?? "")
    }
}

// 在ViewController中实现该代理方法,更新数据源
extension YourViewController: updatingDelegate {
    // ... 其他代理方法
    func whoTextChanged(row: Int, text: String) {
        let dic = resolutionDetailsArray[row] as! NSMutableDictionary
        dic["whoText"] = text
    }
}

额外注意点

  • 尽量避免在cell中直接存储状态,所有可变状态都应该和数据源绑定,cell只负责根据数据源展示UI。
  • 如果你的数据源是不可变的,建议创建一个专门的模型类(比如用struct)来存储数据和状态,代码会更安全易维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 08:17:35