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

UITabBarController中UINavigationController初始未完全包裹UIViewController问题

UITabBarController作为根控制器时,首次启动UINavigationController未完全包裹根VC的问题解决

我把UITabBarController设置为根视图控制器,每个标签页对应一个UINavigationController,其初始UIViewController背景设为红色。但首次启动时,UINavigationController并没有完全包裹这个UIViewController,切换标签页后就恢复正常了。请问这个问题出在哪里?

附上相关代码:

AppDelegate 代码

window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = TabBarViewController()
window?.makeKeyAndVisible()

TabBarViewController 代码

class TabBarViewController: UITabBarController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // Navigation Tab
        let navVC = NavigationViewController()
        // Departure Tab
        let depVC = DeparturesViewController()
        // Settings Tab
        let setVC = SettingsViewController()
        self.viewControllers = [
            createNavigationController(title: "Navigation", rootViewController: navVC, imageName: "map"),
            createNavigationController(title: "Abfahrten", rootViewController: depVC, imageName: "station"),
            createNavigationController(title: "Einstellungen", rootViewController: setVC, imageName: "user"),
        ]
    }
    private func createNavigationController(title: String, rootViewController: UIViewController, imageName: String) -> UINavigationController {
        rootViewController.title = title
        let nc = UINavigationController(rootViewController: rootViewController)
        nc.title = title
        nc.view.backgroundColor = .white
        nc.navigationBar.prefersLargeTitles = true
        nc.navigationController?.navigationItem.largeTitleDisplayMode = .always
        nc.tabBarItem.image = UIImage(named: imageName)?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
        return nc
    }
    override func viewWillAppear(_ animated: Bool) {
        self.selectedIndex = 0
    }
}
class NavigationViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = .red
    }
}

问题原因分析

这个问题的核心出在大标题(Large Title)的设置错误,以及首次启动时的布局时机问题:

  1. 无效的大标题设置代码:
    在createNavigationController方法里,你写了nc.navigationController?.navigationItem.largeTitleDisplayMode = .always,但这里的nc本身就是UINavigationController,它的navigationController属性是nil(只有当导航控制器被另一个导航控制器包裹时才会有值),所以这行代码完全没有生效。这会导致首次启动时,导航栏的大标题布局没有被正确配置,进而影响根视图控制器的view布局范围。

  2. 首次启动的布局时机:
    在UITabBarController的viewDidLoad方法中创建并设置子控制器时,视图的布局约束还没有完全计算完成。首次渲染时,导航栏的高度和布局范围没有被正确传递给根VC,导致根VC的view没有占满导航控制器的可用区域;而切换标签页时,视图会触发重新布局,此时所有约束和布局参数都已正确计算,所以显示恢复正常。

解决方案

只需要修改createNavigationController方法中的大标题设置代码,将大标题的显示模式设置在根视图控制器的navigationItem上,而不是无效的nc.navigationController上:

private func createNavigationController(title: String, rootViewController: UIViewController, imageName: String) -> UINavigationController {
    rootViewController.title = title
    // 给根VC的navigationItem设置大标题显示模式
    rootViewController.navigationItem.largeTitleDisplayMode = .always
    let nc = UINavigationController(rootViewController: rootViewController)
    nc.title = title
    nc.view.backgroundColor = .white
    nc.navigationBar.prefersLargeTitles = true
    nc.tabBarItem.image = UIImage(named: imageName)?.withRenderingMode(UIImage.RenderingMode.alwaysTemplate)
    return nc
}

另外,还有一个小优化:viewWillAppear中的self.selectedIndex = 0可以移除,因为UITabBarController默认的selectedIndex就是0,这行代码没有必要。

修改后,首次启动时导航栏的大标题布局会被正确配置,根VC的view也会正常填充导航控制器的可用区域,不会再出现布局异常的问题。


内容的提问来源于stack exchange,提问作者Lukas Kirner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 05:08:30