iPadOS 18进阶TabBar模式下导航标题自动隐藏问题问询
iPad展开SplitView时TabBarController内导航栏标题/按钮不可见
问题现象
- 在iOS 18环境下,
UITabBarController启用.tabSidebar模式,每个标签页嵌套UISplitViewController时,展开或折叠SplitView后,导航栏的标题及导航栏按钮在UI中不可见,但通过视图层级检查可确认这些元素实际存在。 - 该问题在直接使用
UITabBarController子类,或把它作为子控制器嵌入时均会复现。
复现代码
class TabBarController: UITabBarController,UISplitViewControllerDelegate { override func viewDidLoad() { super.viewDidLoad() let tabTitles = ["First", "Second", "Third", "Fourth", "Fifth", "Sixth"] if #available(iOS 18.0, *) { mode = .tabSidebar var tabs = [UITab]() for title in tabTitles { let tab = UITab(title: title, image: UIImage(systemName: "leaf"), identifier: title, viewControllerProvider: {_ in return self.createSplitViewController() }) tabs.append(tab) } setTabs(tabs, animated: true) } } func createSplitViewController() -> UISplitViewController { let splitViewController = UISplitViewController() splitViewController.preferredDisplayMode = .oneBesideSecondary let masterViewController = UINavigationController(rootViewController: MasterViewController()) let detailViewController = UINavigationController(rootViewController: DetailViewController()) masterViewController.navigationBar.backgroundColor = .red detailViewController.navigationBar.backgroundColor = .blue splitViewController.viewControllers = [masterViewController, detailViewController] return splitViewController } }
解决方案
方法1:监听SplitView显示模式变化,强制刷新导航栏布局
给UISplitViewController设置代理,在显示模式变更时手动触发导航栏的布局更新,修复布局偏移问题:
// 在TabBarController中实现UISplitViewControllerDelegate的方法 func splitViewController(_ svc: UISplitViewController, willChangeToDisplayMode displayMode: UISplitViewController.DisplayMode) { // 遍历SplitView内的所有导航控制器,强制刷新布局 for nav in svc.viewControllers.compactMap({ $0 as? UINavigationController }) { nav.navigationBar.setNeedsLayout() nav.navigationBar.layoutIfNeeded() } } // 同时在createSplitViewController方法中设置代理 func createSplitViewController() -> UISplitViewController { let splitViewController = UISplitViewController() splitViewController.preferredDisplayMode = .oneBesideSecondary splitViewController.delegate = self // 添加这行代码 let masterViewController = UINavigationController(rootViewController: MasterViewController()) let detailViewController = UINavigationController(rootViewController: DetailViewController()) masterViewController.navigationBar.backgroundColor = .red detailViewController.navigationBar.backgroundColor = .blue splitViewController.viewControllers = [masterViewController, detailViewController] return splitViewController }
方法2:禁用TabBarController的自动滚动视图插入调整
iOS 18的.tabSidebar模式下,系统可能自动调整导航栏的内边距,导致内容被遮挡。可以在TabBarController的viewDidLoad中添加以下代码:
override func viewDidLoad() { super.viewDidLoad() // ... 原有代码 ... if #available(iOS 18.0, *) { automaticallyAdjustsScrollViewInsets = false } }
方法3:使用自定义标题视图替代系统标题
如果前两种方法无效,直接自定义导航栏标题视图,绕过系统布局问题:
在MasterViewController和DetailViewController的viewDidLoad中添加:
override func viewDidLoad() { super.viewDidLoad() // 自定义标题视图 let titleLabel = UILabel() titleLabel.text = "你的页面标题" titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold) titleLabel.textColor = .label // 适配深色模式 navigationItem.titleView = titleLabel // 若有导航栏按钮,也可手动创建并添加 let rightBarButton = UIBarButtonItem(title: "操作", style: .plain, target: self, action: #selector(handleAction)) navigationItem.rightBarButtonItem = rightBarButton } @objc private func handleAction() { // 按钮点击逻辑 }
内容的提问来源于stack exchange,提问作者iroh
相关产品推荐
相关产品推荐

