iOS11双UITableView场景下导航栏仅对首个列表折叠问题
解决iOS11大标题模式下多TableView切换时导航栏不折叠的问题
这个问题我之前踩过坑!本质是iOS11+的大标题折叠逻辑和当前界面中被导航控制器识别的活跃UIScrollView绑定——默认只会把第一个加载完成的TableView设为关联对象,切换显示第二个TableView时,导航控制器没更新这个关联,所以滚动第二个列表时不会触发大标题折叠。
下面是几个亲测有效的解决方案,按推荐程度排序:
方案1:切换TableView时手动更新导航控制器的滚动视图关联(最直接)
在你点击分段控件切换TableView显隐的代码里,加入关键逻辑告诉导航控制器当前活跃的滚动视图:
// 假设你的两个TableView分别是tableViewA和tableViewB @IBAction func segmentedControlValueChanged(_ sender: UISegmentedControl) { let isShowingA = sender.selectedSegmentIndex == 0 tableViewA.isHidden = !isShowingA tableViewB.isHidden = isShowingA // 核心:更新导航控制器关联的滚动视图 guard let navController = navigationController else { return } let activeTableView = isShowingA ? tableViewA : tableViewB // 重置安全区域偏移,避免布局错乱 navController.topViewController?.additionalSafeAreaInsets = .zero // 强制触发滚动视图的状态更新,让导航栏识别它 activeTableView.setContentOffset(activeTableView.contentOffset, animated: false) // 刷新导航栏外观 navController.scrollEdgeAppearance = navController.standardAppearance navController.navigationBar.layoutIfNeeded() }
另外要确保两个TableView的内容偏移行为设置正确(默认是.automatic,显式设置更稳妥):
tableViewA.contentInsetAdjustmentBehavior = .automatic tableViewB.contentInsetAdjustmentBehavior = .automatic
方案2:自定义导航控制器,自动识别显示的TableView
如果项目里有多个类似场景,可以封装自定义导航控制器,自动监听当前显示的滚动视图:
class CustomNavigationController: UINavigationController, UIScrollViewDelegate { override func viewDidLoad() { super.viewDidLoad() delegate = self } func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) { // 遍历子视图找到当前显示的TableView let visibleScrollViews = viewController.view.subviews.compactMap { $0 as? UIScrollView }.filter { !$0.isHidden } guard let activeScrollView = visibleScrollViews.first else { return } activeScrollView.delegate = self // 触发状态更新,让导航栏关联这个滚动视图 activeScrollView.setContentOffset(activeScrollView.contentOffset, animated: false) } // 实现滚动代理,确保导航栏响应滚动事件 func scrollViewDidScroll(_ scrollView: UIScrollView) { navigationBar.sizeToFit() } }
把你的控制器放到这个自定义导航控制器里,切换TableView时就不用额外写代码了。
方案3:用单个TableView切换数据源(最优雅的长期方案)
如果两个TableView的UI结构差异不大,推荐只保留一个TableView,通过切换数据源和代理实现切换效果,从根源避免多滚动视图的冲突:
// 定义两个数据源 var dataSourceA: [String] = [] var dataSourceB: [String] = [] var currentDataSource: [String] = [] @IBAction func segmentedControlValueChanged(_ sender: UISegmentedControl) { currentDataSource = sender.selectedSegmentIndex == 0 ? dataSourceA : dataSourceB tableView.reloadData() } // TableView代理方法 func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return currentDataSource.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) cell.textLabel?.text = currentDataSource[indexPath.row] return cell }
这个方案不仅解决了大标题折叠问题,还简化了视图层级,优化了性能,适合UI结构相近的场景。
内容的提问来源于stack exchange,提问作者Kristijan Delivuk
相关产品推荐
相关产品推荐

