Search code examples
iosswiftswiftuiuitabbarcontrolleruitabbar

Present SwiftUI view over UITabBar / UITabBarController


I am switching some screens of my app to use SwiftUI and now i have the structure as follows:

window?.rootViewController is a UITabBarController containing 1 UIViewController and 2 UIHostingControllers (to be able to present SwiftUI views)

When i'm trying to present a SwiftUI view over another contained in UITabBarController it doesn't cover the UITabBar.

enter image description here

While I know how to get expected behavior in UIKit i am completely new to SwiftUI and can't solve this problem.

The structure and code i use are as follows:

AppDelegate

    func presentInitialVC() {
        if settings.firstLoad {
            showOnboarding()
        } else {
            let tabBar = initTabBar()
            window?.rootViewController = tabBar
        }
    }

    private func initTabBar() -> UITabBarController {
        let chatsViewController = {
            let tabBarItem = UITabBarItem(title: "Explore", image: R.image.tabChats(), tag: 0)
            tabBarItem.selectedImage = R.image.tabChatsSelected()

            let view = ChatsView() // SwiftUI view
            let hostingVC = UIHostingController(rootView: view)
            hostingVC.tabBarItem = tabBarItem

            return hostingVC
        }()

        let hubViewController = {
            let tabBarItem = UITabBarItem(title: "Hub", image: R.image.tabHub(), tag: 1)

            let view = HubMainView() // SwiftUI view
            let vc = UIHostingController(rootView: view)
            vc.tabBarItem = tabBarItem

            return vc
        }()

        let settingsViewController = {
            let tabBarItem = UITabBarItem(title: "Settings", image: R.image.tabSettings(), tag: 2)
            tabBarItem.selectedImage = R.image.tabSettingsSelected()
            
            let vc = SettingsViewController()
            let navigationVC = UINavigationController(rootViewController: vc)
            navigationVC.tabBarItem = tabBarItem

            return navigationVC
        }()

        let tabBarController = UITabBarController(nibName: nil, bundle: nil)
        tabBarController.tabBar.tintColor = R.color.buttonsColor()
        tabBarController.viewControllers = [chatsViewController, hubViewController, settingsViewController]
        return tabBarController
    }

HubMainView + SharingRulesView

struct HubMainView: View {

    @StateObject var viewModel = HubMainViewModel()
    @State private var showSharingRules = false
    
    var body: some View {
        SharingRulesView(isPresented: showsSharingRules, onOkTapped: { showsSharingRules = false }) {
            ZStack {
                NavigationView {
                    ScrollView {
                        LazyVStack(spacing: 36) {
                            ForEach(mockedSections) { sectionViewModel in
                                HubSectionView(viewModel: sectionViewModel)
                            }
                        }
                        .padding(24)
                        .toolbar {
                            ToolbarItem(placement: .navigationBarLeading) {
                                Text("Hub")
                                    .font(.system(size: 34, weight: .heavy))
                                    .padding(.leading, 7)
                            }
                        }
                    }
                    .background(Color.secondaryBg)
                }
            }
        }
    }
}

// Container to add blur to the HubMainView and show some content
struct SharingRulesView<Content: View>: View {
    var isPresented: Bool
    @State var onOkTapped: VoidClosure
    var content: () -> Content
    
    var body: some View {
        if !isPresented {
            content()
        } else {
            ZStack(alignment: .center) {
                content()
                    .blur(radius: 3)
                Rectangle()
                    .fill(Color.black)
                    .opacity(0.4)
                    .edgesIgnoringSafeArea(.all)
                CustomButton(onTapped: onOkTapped, title: "Ok")
                    .frame(width: 100, height: 59)
            }
        }
    }
}

The only solution on my mind is to rewrite UITabBarController to SwiftUI view and present it from there. Are there any other solutions?


Solution

  • The thing is that in order to show SharingRulesView it should be presented modally as a view controller over full screen not as a subview of HubMainView. Hosting view controller of HubMainView is in hierarchy of UITabViewController, so if you show something inside of HubMainView it will always be under tab bar.