Swift中UITableView添加Section后数据错位问题求助
嘿,我帮你找出了几个导致section数据混乱的关键错误,咱们一个个拆解并修复:
1. 手动维护numberOfSections完全多余且容易出错
你自己声明了一个numberOfSections变量来控制表格的section数量,但其实每个section对应itemsInTable里的一个元素,直接返回数组的count就行。手动维护这个变量很容易出现同步问题(比如你添加元素后手动加1,但如果后续有删除操作,数组长度和这个变量就会不一致)。
修复代码:
删掉你手动定义的numberOfSections变量,把代理方法改成:
func numberOfSections(in tableView: UITableView) -> Int { return itemsInTable.count }
以后添加元素时,只要往数组里append,然后调用tableView.reloadData()就够了,不用再手动改section数量。
2. Section Header错误使用UITableViewCell重用
这是导致你第1个section数据跑到第4个的核心原因!在viewForHeaderInSection里,你用了dequeueReusableCell来获取header视图——tableView的cell是为表格行设计的,header/footer应该用专门的UITableViewHeaderFooterView。错误重用cell会让旧section的内容被带到新section里,出现数据串位的问题。
修复步骤:
- 先把Storyboard/XIB里的"SectionCell"改成
UITableViewHeaderFooterView(或者用代码自定义一个),设置好对应的identifier(比如"SectionHeader")。 - 然后修改
viewForHeaderInSection方法:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { // 改用专门的HeaderFooterView重用 let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "SectionHeader") as! CreationTableHeaderView let model = itemsInTable[section] if model.typeName == "textField" { header.Btn.isHidden = true header.Txt.isHidden = false header.Txt.text = "textField : \(section)" } else if model.typeName == "Button" { header.Btn.isHidden = false header.Txt.isHidden = true header.Btn.setTitle("Button \(section)", for: .normal) header.Btn.isEnabled = false header.Btn.titleLabel?.textColor = .white header.Btn.backgroundColor = .black } return header }
如果暂时不想改HeaderFooterView,至少要避免重用,直接创建新的cell(但不推荐这么做):
let cell = CreationTableViewCell(style: .default, reuseIdentifier: "SectionCell")
3. cellForRowAt逻辑完全搞反:赋值方向错误
你在cellForRowAt里做了itemsInTable[indexPath.section].content.Hint = cell.txt.text这种操作,这完全搞反了!cellForRowAt的作用是用模型数据配置cell,也就是把模型里的值显示到cell上,而不是反过来把cell的空值(初始时textField的text是空的,placeholder只是提示)赋值给模型。这种错误会导致模型数据被覆盖,cell重用时还会把错误的数据写入模型,进一步加剧混乱。
修复方案:
首先,在cellForRowAt里把模型数据赋值给cell:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "RowCell") as! RowTableViewCell cell.selectionStyle = .none let sectionModel = itemsInTable[indexPath.section] if sectionModel.typeName == "textField" { switch indexPath.row { case 0: cell.TitleLbl.text = "Placeholder" cell.EntityLbl.text = "Hint" cell.txt.placeholder = "Placeholder" // 正确:把模型里的Hint值显示到textField cell.txt.text = sectionModel.content.Hint // 设置tag,方便后续识别是哪个section的哪个row cell.txt.tag = indexPath.section * 10 + indexPath.row cell.txt.delegate = self case 1: cell.TitleLbl.text = "پارامتر URL" cell.EntityLbl.text = "Query" cell.txt.placeholder = "پارامتر URL" cell.txt.text = sectionModel.content.Query cell.txt.tag = indexPath.section * 10 + indexPath.row cell.txt.delegate = self case 2: cell.TitleLbl.text = "متن" cell.EntityLbl.text = "Text" cell.txt.placeholder = "متن" cell.txt.text = sectionModel.content.Text cell.txt.tag = indexPath.section * 10 + indexPath.row cell.txt.delegate = self default: break } } else if sectionModel.typeName == "Button" { switch indexPath.row { case 0: cell.TitleLbl.text = "متن" cell.EntityLbl.text = "Label" cell.txt.placeholder = "متن روی دکمه" cell.txt.text = sectionModel.content.Label cell.txt.tag = indexPath.section * 10 + indexPath.row cell.txt.delegate = self case 1: cell.TitleLbl.text = "نوع عملیات" cell.EntityLbl.text = "Kind" cell.txt.placeholder = "اکسل، پای چارت" cell.txt.text = sectionModel.content.Kind cell.txt.tag = indexPath.section * 10 + indexPath.row cell.txt.delegate = self default: break } } return cell }
然后实现UITextFieldDelegate方法,在用户修改textField内容时更新模型:
extension YourViewController: UITextFieldDelegate { func textFieldDidEndEditing(_ textField: UITextField) { // 通过tag解析section和row let section = textField.tag / 10 let row = textField.tag % 10 guard section < itemsInTable.count else { return } var model = itemsInTable[section] switch model.typeName { case "textField": switch row { case 0: model.content.Hint = textField.text case 1: model.content.Query = textField.text case 2: model.content.Text = textField.text default: break } case "Button": switch row { case 0: model.content.Label = textField.text case 1: model.content.Kind = textField.text default: break } default: break } // 把修改后的模型放回数组 itemsInTable[section] = model } }
最后总结
这三个错误里,header重用错误和cell赋值方向错误是导致section数据串位的主要原因,按照上面的方法修正后,你的表格应该就能正常工作了。
内容的提问来源于stack exchange,提问作者Am1rFT

