You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止UISplitViewController将主视图导航栏安全区域扩展至其他视图?

解决UISplitViewController次级视图安全区域被左侧导航栏干扰的问题

问题说明

我开发的iOS应用以UISplitViewController作为根视图控制器,左侧的Primary窄视图是一个UINavigationController。但遇到了一个奇怪的问题:右侧占据大部分屏幕的Secondary视图,其安全区域居然包含了左侧Primary视图中导航栏的区域。如果在右侧自定义分栏视图里嵌入UINavigationController,它的导航栏会被这个额外的安全区域挤得向下偏移。

截图呈现的状态:右侧的"Root View Controller"文字位于Primary视图的导航栏中,中间顶部的蓝色视图用来展示安全区域——它的顶部紧贴屏幕上边缘,底部对齐安全区域顶部;右侧灰色区域是嵌套了UINavigationController的子视图控制器,"Navigation Bar"是该导航控制器根视图的导航项标题。

排查发现

通过视图调试器检查后发现,UISplitViewController会自动将Secondary视图包裹在一个UINavigationController中,正是这个系统自动添加的导航栏导致元素被向下推,该行为在UISplitViewController的官方文档中有相关说明。

测试代码

以下是测试应用中Secondary视图控制器区域的代码:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let safeAreaView = UIView()
        safeAreaView.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(safeAreaView)
        safeAreaView.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
        safeAreaView.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
        safeAreaView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
        safeAreaView.bottomAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
        safeAreaView.backgroundColor = .blue
        
        let container = NavContainerViewController(nibName: nil, bundle: nil)
        self.addChild(container)
        self.view.addSubview(container.view)
        container.didMove(toParent: self)
        container.view.translatesAutoresizingMaskIntoConstraints = false
        container.view.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
        container.view.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
        container.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
        container.view.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.33).isActive = true
    }
}

class NavContainerViewController : UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let child = NavChildController(nibName: nil, bundle: nil)
        let nav = UINavigationController(rootViewController: child)
        self.addChild(nav)
        self.view.addSubview(nav.view)
        nav.didMove(toParent: self)
        nav.view.translatesAutoresizingMaskIntoConstraints = false
        nav.view.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
        nav.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
        nav.view.rightAnchor.constraint(equalTo: self.view.rightAnchor).isActive = true
        nav.view.leftAnchor.constraint(equalTo: self.view.leftAnchor).isActive = true
    }
}

class NavChildController : UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .lightGray
        
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = "Top"
        self.view.addSubview(label)
        label.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
        label.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor).isActive = true
        
        self.navigationItem.title = "Navigation Bar"
    }
}

解决办法

方法1:手动给Secondary视图嵌套NavigationController(推荐)

系统自动包裹NavigationController是问题的根源,因此我们可以自行给Secondary视图嵌套好NavigationController,再传递给UISplitViewController,这样系统就不会额外添加导航控制器,安全区域也不会被干扰。示例代码如下:

// 初始化Primary和Secondary的导航控制器
let primaryNav = UINavigationController(rootViewController: PrimaryViewController())
let secondaryNav = UINavigationController(rootViewController: YourSecondaryViewController())

// 创建SplitViewController并设置子控制器
let splitVC = UISplitViewController(style: .doubleColumn)
splitVC.setViewController(primaryNav, for: .primary)
splitVC.setViewController(secondaryNav, for: .secondary)

// 设置为根控制器
UIApplication.shared.windows.first?.rootViewController = splitVC

方法2:抵消额外的安全区域

如果不想手动嵌套导航控制器,也可以在Secondary视图控制器中调整安全区域,抵消系统自动添加的导航栏带来的顶部偏移:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // 抵消顶部额外的安全区域
        if let navBarHeight = navigationController?.navigationBar.frame.height {
            additionalSafeAreaInsets.top = -navBarHeight
        }
    }
}

或者重写additionalSafeAreaInsets属性:

class ViewController: UIViewController {
    override var additionalSafeAreaInsets: UIEdgeInsets {
        var insets = super.additionalSafeAreaInsets
        insets.top = -navigationController?.navigationBar.frame.height ?? 0
        return insets
    }
}

方法3:调整UISplitViewController的显示设置

还可以通过设置UISplitViewController的preferredDisplayMode来优化显示效果,比如设置为.oneBesideSecondary,确保Primary视图始终显示,同时配合手动嵌套导航控制器使用,效果更稳定:

splitVC.preferredDisplayMode = .oneBesideSecondary

内容的提问来源于stack exchange,提问作者Tom Hamming

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 02:48:10