UINavigationItem.LargeTitleDisplayMode.never在UISplitViewController下仍显示大标题背景异常
iOS 15默认导航栏滚动到顶部时为半透明效果,由于当前视图控制器展示UIMapView,我希望仅在该页面关闭该效果,其余页面保留iOS 15的默认新特性。
我已经将navigationItem.largeTitleDisplayMode设置为.never,相关代码如下:
override func viewDidLoad() { super.viewDidLoad() if #available(iOS 15.0, *) { let appearance = UINavigationBarAppearance() appearance.configureWithDefaultBackground() self.navigationItem.scrollEdgeAppearance = appearance } }
但运行后导航栏仍然显示大标题模式下的背景,我尝试将navigationItem.largeTitleDisplayMode先设为.always再改回.never也无法修复该问题。

我仅在UINavigationController嵌套在UISplitViewController的场景下能复现该问题,结构如下图:
如果不使用UISplitViewController,仅用UINavigationController -> 带按钮的UIViewController -> 带UIMapView的UIViewController的层级时,UINavigationBar表现完全正常。
更奇怪的是,在iOS 14上运行该示例时,UINavigationBar外观正常,但iOS 15上被导航栏覆盖的地图区域无法响应交互。
请问是我哪里配置错误,还是这属于iOS系统本身的Bug?
解决方案
这是iOS 15的已知系统Bug,UISplitViewController嵌套UINavigationController的场景下,单个ViewController的navigationItem属性不会被系统正确读取,会优先使用UINavigationBar的全局配置。你可以按以下步骤修复:
- 把导航栏配置逻辑从
viewDidLoad移到viewWillAppear,确保页面展示时配置能被系统实时读取,同时设置standardAppearance和scrollEdgeAppearance,并同步给当前导航栏:
override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // 强制关闭当前页面大标题 navigationItem.largeTitleDisplayMode = .never navigationController?.navigationBar.prefersLargeTitles = false if #available(iOS 15.0, *) { let appearance = UINavigationBarAppearance() // 需完全不透明就用configureWithOpaqueBackground,保留默认半透用configureWithDefaultBackground appearance.configureWithDefaultBackground() // 单个item的两个外观都要设置,避免场景切换时读取缺省值 navigationItem.standardAppearance = appearance navigationItem.scrollEdgeAppearance = appearance // 同步给导航栏设置,兼容UISplitViewController的特殊逻辑 navigationController?.navigationBar.standardAppearance = appearance navigationController?.navigationBar.scrollEdgeAppearance = appearance } }
- 修复地图交互被遮挡问题:调整MapView的顶部约束对齐安全区顶部,同时关闭布局延伸到不透明栏下方的配置:
override func viewDidLoad() { super.viewDidLoad() extendedLayoutIncludesOpaqueBars = false // 若为代码布局MapView,添加以下约束 mapView.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ mapView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), mapView.leadingAnchor.constraint(equalTo: view.leadingAnchor), mapView.trailingAnchor.constraint(equalTo: view.trailingAnchor), mapView.bottomAnchor.constraint(equalTo: view.bottomAnchor) ]) }
- 在页面消失时还原导航栏配置,保证其他页面可以正常使用iOS 15默认特性:
override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) if #available(iOS 15.0, *) { navigationController?.navigationBar.standardAppearance = UINavigationBarAppearance() navigationController?.navigationBar.scrollEdgeAppearance = nil } navigationController?.navigationBar.prefersLargeTitles = true }
内容的提问来源于stack exchange,提问作者Zandor Smith

