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
相关产品推荐
相关产品推荐

