swiftuikit

Layout constraint error when adding a UINavigationController to a UITabBarController on iPadOS


I have a Xcode 16.3 Swift/UIKit project that does programmtically setup a UITabBarController in SceneDelegate like so:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }

    window = UIWindow(windowScene: windowScene)
    let vc = UIViewController()

    vc.view.backgroundColor = .systemRed
    vc.title = "Hello"
    let nc = UINavigationController(rootViewController: vc)
    let tb = UITabBarController()
    tb.viewControllers = [nc]
    window?.rootViewController = tb
    window?.makeKeyAndVisible()
}

This is giving me layout constraint errors on iPadOS (not on iOS):

Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
(
    "<NSLayoutConstraint:0x6000021318b0 UILayoutGuide:0x600003b0c460'TitleView(layout=0x101c11390)'.trailing <= UILayoutGuide:0x600003b0c7e0'UIViewLayoutMarginsGuide'.trailing   (active)>",
    "<NSLayoutConstraint:0x6000021028f0 H:|-(417)-[UILayoutGuide:0x600003b08000'TabBarGuide(0x101c10e80)'](LTR)   (active, names: '|':_UINavigationBarContentView:0x101c10e80 )>",
    "<NSLayoutConstraint:0x600002100c30 UILayoutGuide:0x600003b08000'TabBarGuide(0x101c10e80)'.width == 0   (active)>",
    "<NSLayoutConstraint:0x6000021005f0 UILayoutGuide:0x600003b0c460'TitleView(layout=0x101c11390)'.trailing >= UILayoutGuide:0x600003b08000'TabBarGuide(0x101c10e80)'.trailing   (active)>",
    "<NSLayoutConstraint:0x600002100b40 'UIView-Encapsulated-Layout-Width' _UINavigationBarContentView:0x101c10e80.width == 0   (active)>",
    "<NSLayoutConstraint:0x60000212d4a0 'UIView-rightMargin-guide-constraint' H:[UILayoutGuide:0x600003b0c7e0'UIViewLayoutMarginsGuide']-(0)-|(LTR)   (active, names: '|':_UINavigationBarContentView:0x101c10e80 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x6000021318b0 UILayoutGuide:0x600003b0c460'TitleView(layout=0x101c11390)'.trailing <= UILayoutGuide:0x600003b0c7e0'UIViewLayoutMarginsGuide'.trailing   (active)>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.

When I simply change from:

tb.viewControllers = [nc]

to:

tb.viewControllers = [vc]

the errors go away.

What is wrong here?


Solution

  • You can work around the issue like this:

        guard let windowScene = (scene as? UIWindowScene) else { return }
    
        window = UIWindow(windowScene: windowScene)
        let vc = UIViewController()
    
        vc.view.backgroundColor = .systemRed
        vc.title = "Hello"
        let nc = UINavigationController(rootViewController: vc)
        let tb = UITabBarController()
        window?.rootViewController = tb
        window?.makeKeyAndVisible()
        tb.viewControllers = [nc]