Swift列表选择后回传并刷新指定TableView单元格问题
Hi there! Your current approach using delegation is definitely the right direction, but there are a few key fixes and best practices we need to apply to make this work reliably, safely, and efficiently. Let's break this down step by step.
First: Fix Your Model Structure
Your original models use let for properties that need to be updated (like textField and items). Since Swift structs are value types, you can't modify let properties after initialization. Let's adjust the model to make mutable properties var:
struct Unit : Codable { var sectionList : [SectionList]? // 改为var支持修改 } struct SectionList : Codable { let title : String? var items : [Item]? // 改为var支持修改 } struct Item : Codable { let actionType : Int? var textField : String? // 改为var支持修改 let textValue : String? let pickList: [SectionList]? let multiSelect: Bool? let selectedValue: [String]? let version: Int? let masterId: Int? let itemValue: String? }
Second: Optimize FirstViewController
We'll fix forced unwrapping issues, handle optional data safely, and refresh only the specific cell instead of the entire table view:
class FirstViewController: UIViewController, ListSelectionDelegate { var selectedIndexPath: IndexPath? // 直接存储完整的IndexPath,比分开存section和row更简洁 // 重命名按钮方法更贴合功能(这应该是跳转到列表选择页的按钮) @IBAction func goToListButtonAction(_ sender: Any) { // 确保用户先选中单元格再跳转 guard let selectedIndexPath = selectedIndexPath else { let alert = UIAlertController(title: "提示", message: "请先选择要修改的项", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) present(alert, animated: true) return } guard let vc = storyboard?.instantiateViewController(withIdentifier: "ListViewController") as? ListViewController else { print("加载ListViewController失败") return } vc.delegate = self navigationController?.pushViewController(vc, animated: true) } // 实现代理方法,安全处理数据更新 func didSelectListValue(_ value: String) { guard let indexPath = selectedIndexPath, var currentUnit = AppData, var sections = currentUnit.sectionList, indexPath.section < sections.count, var items = sections[indexPath.section].items, indexPath.row < items.count else { print("无法更新数据:索引越界或数据为空") return } // 修改指定Item的textField值 items[indexPath.row].textField = value // 将修改后的数据逐层存回模型 sections[indexPath.section].items = items currentUnit.sectionList = sections AppData = currentUnit // 只刷新被修改的单元格(比reloadData()性能更好) tableView.reloadRows(at: [indexPath], with: .automatic) } func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { selectedIndexPath = indexPath // 可选:取消选中状态,提升用户体验 tableView.deselectRow(at: indexPath, animated: true) } }
Third: Improve ListViewController (Formerly SecondViewController)
We'll fix memory leaks with weak delegation, simplify selection logic, and add proper single-selection behavior:
// 重命名协议更清晰,同时继承AnyObject以支持弱引用代理 protocol ListSelectionDelegate: AnyObject { func didSelectListValue(_ value: String) } class ListViewController: UIViewController { // 使用weak修饰代理,避免循环引用(视图控制器间代理的关键) weak var delegate: ListSelectionDelegate? // 替换成你实际的列表数据 private let listOptions = ["选项1", "选项2", "选项3", "选项4"] // MARK: - UITableView 数据源 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return listOptions.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "ListCell", for: indexPath) cell.textLabel?.text = listOptions[indexPath.row] return cell } // MARK: - UITableView 代理 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let selectedValue = listOptions[indexPath.row] // 更新选中单元格的UI if let selectedCell = tableView.cellForRow(at: indexPath) { selectedCell.accessoryType = .checkmark } // 取消其他单元格的选中状态(单选场景) tableView.visibleCells.forEach { cell in guard let cellIndexPath = tableView.indexPath(for: cell), cellIndexPath != indexPath else { return } cell.accessoryType = .none } // 将选中值回传给FirstViewController delegate?.didSelectListValue(selectedValue) // 返回上一页 navigationController?.popViewController(animated: true) } }
Key Things to Remember
- 避免内存泄漏: 代理属性一定要用
weak修饰,同时协议要继承AnyObject——这能防止视图控制器之间互相持有导致的内存泄漏。 - 安全处理可选值: 永远不要用
!强制解包,用guard let或if let安全地处理可选值,避免运行时崩溃。 - 高效刷新: 使用
reloadRows(at:with:)代替reloadData(),只更新需要修改的单元格,这会让界面更流畅。 - 清晰命名: 重命名控制器、协议和方法以反映它们的功能(比如
SecondViewController改为ListViewController),让代码更易维护。
内容的提问来源于stack exchange,提问作者user12435629

