Search code examples
iosswiftswiftuiuikituihostingcontroller

Overlay SwiftUI Color over UINavigation tabbar when using HostingController


I have a swiftUI view embed in UIhostingController with UInavigation. What I'm trying to achieve is that when I'm presenting a .sheet overlay I want to overlay remainder of the screen with black color with opacity.

something like this:

    public var body: some View {
    ZStack {
        Color.color(for: ColorToken.Background._02, colorScheme: colorScheme)
            .ignoresSafeArea()
        mainContent
            .padding(.top)
        if store.bottomSheetPosition != .hidden {
            Color.black
                .opacity(0.6)
                .ignoresSafeArea()
                .transition(.opacity)
                .contentShape(Rectangle())
                .onTapGesture {
                    send(.didTapDismissBottomSheet, animation: .easeOut(duration: 0.3))
                }
        }
    }

Problem that I'm facing right now is that even tho it covers remainder screen with color, navigation buttons are still on the top of the view heirarchy, so they are not covered and are tappable. it looks like this:

enter image description here

What are my options?


Solution

  • The issue occurs because UIHostingController and SwiftUI view hierarchy are being used in conjunction with UINavigationController. The navigation bar buttons remain at the top of the hierarchy (belonging to the UIKit navigation controller) and are not covered by your ZStack or Color.black overlay in SwiftUI. To address this, the approach with UIVisualEffectView and a custom UIViewRepresentable is a good solution. Here's how to implement this properly:

    1. Create the VisualEffectView struct

    This will wrap a UIVisualEffectView in SwiftUI:

    import SwiftUI
    
    struct VisualEffectView: UIViewRepresentable {
        var effect: UIVisualEffect?
    
        func makeUIView(context: Context) -> UIVisualEffectView {
            let view = UIVisualEffectView(effect: effect)
            DispatchQueue.main.async {
                /**
                 Set the background color of the superviews (if needed)
                 */
                view.superview?.superview?.backgroundColor = .clear
            }
            return view
        }
    
        func updateUIView(_ uiView: UIVisualEffectView, context: Context) {
            uiView.effect = effect
        }
    }
    

    2. Use VisualEffectView in Your SwiftUI View

    You can now utilize the VisualEffectView with a UIBlurEffect in your ZStack to create a smooth overlay. Here's an example with your current layout: ` import SwiftUI

        struct ContentView: View{
            @Environment(\.colorScheme) var colorScheme
            @State private var isSheetShown = false
            var body: some View {
                ZStack {
                    // Background of the view
                    Color.color(for: ColorToken.Background._02, colorScheme: colorScheme)
                        .ignoresSafeArea()
        
                    // Your main content
                    VStack {
                        Text("Main Content")
                            .padding()
        
                        Button("Present Bottom Sheet") {
                            isSheetShown = true
                        }
                        .padding()
                    }
        
                    // Overlay when the sheet is shown
                    if isSheetShown {
                        ZStack {
                            // Visual Effect View
                            VisualEffectView(effect: UIBlurEffect(style: .dark))
                                .ignoresSafeArea()
        
                            // Black transparent overlay on top of the blur effect
                            Color.black
                                .opacity(0.6)
                                .ignoresSafeArea()
                        }
                        .transition(.opacity) // Add fade transition for smoothness
                    }
                }
            }
    }