iosswiftswiftuimodalviewcontroller

Is it possible to make the "Presentation Hosting Controller" background color transparent instead of white color in SwiftUI?


I am trying to make a "FullScreenModalView" where you can send any view with a fixed height and width and it will show with a modal presentation. It is working perfectly but I can't make the "Presentation Hosting Controller" background color transparent. I tried a couple of solutions like setting a custom transparent view as a background view, but still it has no effect on the "Presentation Hosting Controller" background color.

What I want to achieve:
enter image description here

But in the view hierarchy I can see the background color of the Presentation Hosting Controller is white, which I need to set as a clear color.
enter image description here

My code:
ContentView

import SwiftUI

struct ContentView: View {
    @State private var isShowingCommonMenu = false
    @State private var isPresented = false
    
    var body: some View {
        VStack {
            Button {
                //MARK: Show Network Configure List
                self.isShowingCommonMenu.toggle()
            } label: {
                Text("GO")
            }
            .fullScreenCover(isPresented: $isShowingCommonMenu) {
                NavigationStack {
                    GeometryReader { geometry in
                        FullScreenModalView(isShowingCommonMenu: $isShowingCommonMenu, childViewWidth: (geometry.size.width - geometry.size.width/10), childViewHeight: geometry.size.height) {
                            TheViewToBeLoadedView()
                        }
                    }
                }
            }
        }
        .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
        .background(Color.orange)
        .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

FullScreenModalView

import SwiftUI

struct FullScreenModalView<Content: View>: View {
    @Binding var isShowingCommonMenu: Bool
    var childViewWidth: CGFloat
    var childViewHeight: CGFloat
    let content: () -> Content
    
    var body: some View {
        ZStack {
            Color.black.opacity(0.4)
                .edgesIgnoringSafeArea(.all)
                .onTapGesture {
                    //Dismiss the modal view when the user taps outside of it
                    dismiss()
                }
            
            VStack {
                content()
                    .background(ClearBackgroundView())
            }
            .frame(width: childViewWidth, height: childViewHeight)
            .background(Color.white)
            .cornerRadius(10)
            .overlay(
                Button {
                    dismiss()
                } label: {
                    DismissButton()
                }, alignment: .topTrailing)
        }
        .ignoresSafeArea(.keyboard)
        .background(TransparentBackground())
    }
    
    private func dismiss() {
        isShowingCommonMenu = false
    }
}

struct FullScreenModalView_Previews: PreviewProvider {
    static var previews: some View {
        FullScreenModalView(isShowingCommonMenu: .constant(true), childViewWidth: 200, childViewHeight: 200) {
            Text("This is a custom preview view")
        }
    }
}
struct TransparentBackground: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        let view = UIView()
        DispatchQueue.main.async {
            view.superview?.superview?.backgroundColor = .clear
        }
        return view
    }

    func updateUIView(_ uiView: UIView, context: Context) {}
}

struct DismissButton: View {
    var body: some View {
        ZStack {
            Circle()
                .frame(width: 30, height: 30)
                .foregroundColor(.white)
                .opacity(0.6)
            Image(systemName: "xmark")
                .imageScale(.medium)
                .frame(width: 44, height: 44)
                .foregroundColor(Color(.label))
        }
    }
}

And the sample view that I want to load:

import SwiftUI

struct TheViewToBeLoadedView: View {
    var body: some View {
        GeometryReader { geometry in
            Text("TheViewToBeLoadedView")
                .frame(width: geometry.size.width, height: geometry.size.height, alignment: .center)
                .background(Color.yellow)
        }
    }
}

struct TheViewToBeLoadedView_Previews: PreviewProvider {
    static var previews: some View {
        TheViewToBeLoadedView()
    }
}

Solution

  • Apply .background(TransparentBackground()) on the Content in .fullScreenCover

    e.g.

    struct ContentView: View {
        @State private var showingFullscreenModal: Bool = false
    
        var body: some View {
            VStack {
                Button {
                    showingFullscreenModal.toggle()
                } label: {
                    Text("Show Fullscreen Modal")
                }
            }
            .frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
            .background(.orange)
    
            .fullScreenCover(isPresented: $showingFullscreenModal) {
                SubView(showing: $showingFullscreenModal, width: 300, height: 400)
                    .background(TransparentBackground()) // <--- Here
            }
        }
    }
    
    struct TransparentBackground: UIViewRepresentable {
        func makeUIView(context: Context) -> UIView {
            let view = UIView()
            DispatchQueue.main.async {
                view.superview?.superview?.backgroundColor = .clear
            }
            return view
        }
    
        func updateUIView(_ uiView: UIView, context: Context) {}
    }
    
    
    struct SubView: View {
        @Binding var showing: Bool
        
        let width: CGFloat
        let height: CGFloat
        
        var body: some View {
            VStack {
                Text("Hello World")
                
                Button {
                    showing = false
                } label: {
                    Text("Close")
                }
            }
            .frame(width: width, height: height)
            .background(.teal)
        }
    }
    

    enter image description here

    So, in your case, apply .background(TransparentBackground()) on NavigationStack in .fullScreenCover