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

如何在嵌套UIHostingController的UITabBarController实现全屏覆盖加载视图

问题描述

我有一个包含2个项目的UITabBarController,第一个视图控制器是嵌套了承载SwiftUI HomeView的UIHostingController的UINavigationController,HomeView可通过NavigationLink跳转到SecondView。现在需要从SecondView触发一个全屏覆盖的加载视图,要求覆盖整个界面(包括TabBar),且方案需支持iOS 13及以上,求最佳实现方式。

相关代码

App启动配置代码

tabBarCnt.tabBar.tintColor = UIColor.black

let firstVc = UINavigationController(rootViewController: UIHostingController(rootView: HomeView()))
firstVc.title = "First"
let secondVc = UIViewController()
secondVc.title = "Second"
tabBarCnt.setViewControllers([firstVc, secondVc], animated: false)

UIApplication.shared.windows.first?.rootViewController = tabBarCnt
UIApplication.shared.windows.first?.makeKeyAndVisible()

HomeView代码

struct HomeView: View {
    var body: some View {
        NavigationView {
            VStack{
                Text("Home View")
                NavigationLink {
                    SecondView()
                } label: {
                    Text("Press here to go to second page")
                }
            }
        }
        .navigationBarTitle("NAVIGATION TITLE")
    }
}

SecondView代码

struct SecondView: View {
    var body: some View {
        VStack(spacing: 20) {
            Text("Second View")
            Text("How can I make a modal view that covers the entire view and the tabBar ??? the View should get triggered from there. BAsically I want to make a loading view that covers everything.")
        }
    }
}
解决方案

方案一:SwiftUI全屏模态+UIKit样式配置(兼容iOS13+)

利用SwiftUI的fullScreenCover组件,手动配置UIKit模态样式确保覆盖TabBar:

  1. 先定义加载视图:
struct LoadingView: View {
    var body: some View {
        ZStack {
            Color.black.opacity(0.7)
                .ignoresSafeArea()
            VStack {
                ProgressView()
                    .scaleEffect(1.5)
                    .tint(.white)
                Text("加载中...")
                    .foregroundColor(.white)
                    .padding(.top, 16)
            }
        }
    }
}
  1. 修改SecondView添加触发逻辑:
struct SecondView: View {
    @State private var isLoading = false
    
    var body: some View {
        VStack(spacing: 20) {
            Text("Second View")
            Button("触发全屏加载") {
                isLoading = true
                // 模拟加载过程,3秒后关闭
                DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                    isLoading = false
                }
            }
        }
        .fullScreenCover(isPresented: $isLoading) {
            LoadingView()
        }
        .onAppear {
            // 强制设置全屏模态样式,确保覆盖TabBar
            if let hostingVc = UIApplication.shared.windows.first?.rootViewController?.presentedViewController as? UIHostingController<LoadingView> {
                hostingVc.modalPresentationStyle = .fullScreen
            }
        }
    }
}

方案二:直接向App主窗口添加加载视图(最彻底)

完全脱离控制器层级,直接将加载视图挂载到应用主窗口,确保覆盖所有界面元素:

  1. 创建UIKit加载容器控制器:
class LoadingOverlayViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.black.withAlphaComponent(0.7)
        // 嵌入SwiftUI加载视图
        let hostingVc = UIHostingController(rootView: LoadingView())
        addChild(hostingVc)
        hostingVc.view.frame = view.bounds
        view.addSubview(hostingVc.view)
        hostingVc.didMove(toParent: self)
    }
    
    // 显示加载视图
    func show() {
        guard let window = UIApplication.shared.windows.first else { return }
        window.addSubview(view)
        view.frame = window.bounds
    }
    
    // 隐藏加载视图
    func hide() {
        view.removeFromSuperview()
    }
}
  1. 在SecondView中调用:
struct SecondView: View {
    let loadingOverlay = LoadingOverlayViewController()
    
    var body: some View {
        VStack(spacing: 20) {
            Text("Second View")
            Button("触发全屏加载") {
                loadingOverlay.show()
                DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                    loadingOverlay.hide()
                }
            }
        }
    }
}

方案三:环境变量传递顶层控制器(解耦设计)

通过SwiftUI环境对象注入顶层UITabBarController,从顶层控制器弹出全屏模态:

  1. 定义环境对象并在启动时注入:
// 定义共享状态对象
class AppState: ObservableObject {
    let rootTabBarController: UITabBarController
    init(tabBar: UITabBarController) {
        self.rootTabBarController = tabBar
    }
}

// 修改App启动代码
let tabBarCnt = UITabBarController()
// ... 原有配置代码 ...

// 创建环境对象并注入HomeView
let appState = AppState(tabBar: tabBarCnt)
let firstVc = UINavigationController(rootViewController: UIHostingController(rootView: HomeView().environmentObject(appState)))
  1. 在SecondView中使用环境对象:
struct SecondView: View {
    @EnvironmentObject var appState: AppState
    
    var body: some View {
        VStack(spacing: 20) {
            Text("Second View")
            Button("触发全屏加载") {
                let loadingVc = UIHostingController(rootView: LoadingView())
                loadingVc.modalPresentationStyle = .fullScreen
                // 从顶层TabBarController弹出模态
                appState.rootTabBarController.present(loadingVc, animated: true)
                DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
                    loadingVc.dismiss(animated: true)
                }
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者P S

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 16:45:22