TableView重载分区崩溃求助:NSInternalInconsistencyException异常
问题分析
你的崩溃报错attempt to delete section 100, but there are only 7 sections before the update,核心原因是:
prevIousSelection初始值设为了不存在的section编号100,当第一次执行折叠逻辑时,你尝试给这个无效的section设置状态,虽然字典允许存储该键,但这会干扰tableView的状态判断。- 当展开新section时,你只重新加载了当前section,没有重新加载之前展开的section,导致tableView的行数缓存与数据源不一致,引发潜在的崩溃风险。
- 头部视图复用逻辑错误:你在
viewForHeaderInSection中始终复用同一个headerViewMain实例,导致所有section的头部视图共享同一个对象,点击时可能传递错误的section编号。
修复代码
以下是修正后的完整代码:
class ExploreLocallyVc: UIViewController { @IBOutlet weak var tableView: UITableView? // 存储每个section的折叠状态,默认所有section都是收起(false) var sectionCollapsedStatus: [Int: Bool] = [:] // 初始值设为nil,表示之前没有选中任何section var previouslySelectedSection: Int? = nil override func viewDidLoad() { super.viewDidLoad() tableView?.estimatedRowHeight = 80 tableView?.rowHeight = UITableViewAutomaticDimension tableView?.sectionHeaderHeight = 60 tableView?.separatorStyle = .none // 注册头部视图的nib,避免重复加载 tableView?.register(UINib(nibName: "HeaderView", bundle: nil), forHeaderFooterViewReuseIdentifier: "HeaderView") } } extension ExploreLocallyVc: UITableViewDelegate, UITableViewDataSource { func numberOfSections(in tableView: UITableView) -> Int { return 7 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { // 如果字典中没有该section的状态,默认返回0(收起) guard let isExpanded = sectionCollapsedStatus[section], isExpanded else { return 0 } return 4 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "q", for: indexPath) cell.textLabel?.text = "\(indexPath.row)" return cell } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { // 复用头部视图,而不是每次创建同一个实例 guard let headerView = tableView.dequeueReusableHeaderFooterView(withIdentifier: "HeaderView") as? HeaderView else { return UIView() } headerView.titleLabel?.text = "\(section)" // 根据当前section的状态设置箭头方向 let isExpanded = sectionCollapsedStatus[section] ?? false headerView.arrowLabel?.transform = isExpanded ? CGAffineTransform(rotationAngle: .pi) : .identity headerView.section = section headerView.delegate = self return headerView } } extension ExploreLocallyVc: HeaderViewDelegate { func toggleSection(header: HeaderView, section: Int) { // 获取当前section的状态,默认是收起(false) let currentIsExpanded = sectionCollapsedStatus[section] ?? false if currentIsExpanded { // 折叠当前section sectionCollapsedStatus[section] = false tableView?.reloadSections(IndexSet(integer: section), with: .automatic) } else { // 展开当前section,同时折叠之前的section(如果存在且不是当前section) if let prevSection = previouslySelectedSection, prevSection != section { sectionCollapsedStatus[prevSection] = false // 重新加载之前的section,更新行数 tableView?.reloadSections(IndexSet(integer: prevSection), with: .automatic) } // 展开当前section sectionCollapsedStatus[section] = true tableView?.reloadSections(IndexSet(integer: section), with: .automatic) // 更新之前选中的section为当前section previouslySelectedSection = section } print(sectionCollapsedStatus) } }
关键修复点
- 修正初始值问题:将
prevIousSelection(重命名为previouslySelectedSection更清晰)的初始值设为nil,避免操作不存在的section。 - 修复头部视图复用:使用
tableView.dequeueReusableHeaderFooterView复用头部视图,每个section拥有独立的头部实例,避免section编号被覆盖。 - 同步更新tableView状态:当展开新section时,不仅更新之前section的状态,还重新加载该section,确保tableView的行数与数据源一致。
- 优化状态判断:通过空合运算符
??处理字典中不存在的section状态,默认返回收起(false),逻辑更简洁。
内容的提问来源于stack exchange,提问作者Abhiram
相关产品推荐
相关产品推荐

