Expandable Collapse TableView固定行至顶部异常及NSRangeException报错求助
TableView折叠展开问题与NSRangeException异常修复
问题描述
我做了一个可展开折叠的TableView,展开时能把行固定在顶部,但关闭顶部已展开的行后,该行会回到原位置,我希望关闭后它仍保持在顶部。现在我的hideSection函数触发了NSRangeException异常,报错信息如下:
Terminating app due to uncaught exception 'NSRangeException', reason: 'Attempted to scroll the table view to an out-of-bounds row'
相关代码:
@objc private func hideSection(sender: UIButton) { let section = sender.tag let index = IndexPath(row: 0, section: section) func indexPathsForSection() -> [IndexPath] { var indexPaths = [IndexPath]() for row in 0..<self.tableViewData[section].count { indexPaths.append(IndexPath(row: row, section: section)) } return indexPaths } tableViewContentOffset = tableView.contentOffset if self.hiddenSections.contains(section) { self.hiddenSections.remove(section) self.tableView.insertRows(at: indexPathsForSection(), with: .fade) if tableView.numberOfRows(inSection: section) > 0 { tableView.scrollToRow(at: index, at: .top, animated: true) } } else { self.hiddenSections.insert(section) self.tableView.deleteRows(at: indexPathsForSection(), with: .fade) tableView.scrollToRow(at: index, at: .top, animated: true) } }
问题分析与修复
1. 异常根源
在折叠分支(hiddenSections.insert(section))中,你先删除了该section的所有行,紧接着调用scrollToRow(at: index, at: .top, animated: true)——此时该section已经没有任何行,IndexPath(row:0, section:section)属于越界索引,直接触发NSRangeException。
2. 实现“关闭后保持顶部”的正确逻辑
要让关闭后的section停在顶部,不能滚动到已删除的行,而是要直接定位到section的头部位置。修复后的代码如下:
@objc private func hideSection(sender: UIButton) { let section = sender.tag let index = IndexPath(row: 0, section: section) func indexPathsForSection() -> [IndexPath] { var indexPaths = [IndexPath]() for row in 0..<self.tableViewData[section].count { indexPaths.append(IndexPath(row: row, section: section)) } return indexPaths } // 提前获取section头部的位置,用于折叠后定位 let sectionHeaderRect = tableView.rectForHeader(inSection: section) let targetOffset = CGPoint(x: 0, y: sectionHeaderRect.origin.y - tableView.contentInset.top) if self.hiddenSections.contains(section) { self.hiddenSections.remove(section) self.tableView.insertRows(at: indexPathsForSection(), with: .fade) if tableView.numberOfRows(inSection: section) > 0 { tableView.scrollToRow(at: index, at: .top, animated: true) } } else { self.hiddenSections.insert(section) self.tableView.deleteRows(at: indexPathsForSection(), with: .fade) // 滚动到section头部,替代无效的scrollToRow调用 tableView.setContentOffset(targetOffset, animated: true) } }
关键修改点
- 折叠操作前,先计算section头部的准确偏移量,包含导航栏等内边距的修正
- 折叠完成后,用
setContentOffset直接定位到section头部,既避免了越界异常,又实现了“保持顶部”的需求
内容的提问来源于stack exchange,提问作者Burak Köse
相关产品推荐
相关产品推荐

