Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to set a fullScreenCover background opacity?

Tags:

ios

swiftui

I need to push a modal view from a button inside a view component, but should only be covering the bottom half of the screen height, the top half a semi transparent background(black with opacity 30%). Setting the opacity for the topmost view inside the fullscreenCover view builder doesnt work. Any help would be appreciated.

struct ContentView: View {
    
    @State var present: Bool = false
    var body: some View {
        
        VStack(spacing: 20) {
            
            Button(action: {
                present = true
            }, label: {
                
                Text("spawn translucent modal")
            })
            .fullScreenCover(isPresented: $present) {
                VStack(spacing: 20) {
                    Spacer()
                        .frame(maxWidth: .infinity, minHeight: 100)
                        .background(Color.black)
                        .opacity(0.3)
                    
                    Text("modal")
                }
                .background(Color.clear)
                
            }
            
            Text("some content")
            Text("some more content")
        }
    }
}
like image 820
Aswath Avatar asked Dec 31 '22 18:12

Aswath


2 Answers

Here is possible solution

        .fullScreenCover(isPresented: $present) {
            VStack(spacing: 20) {
                Spacer()
                    .frame(maxWidth: .infinity, minHeight: 100)
                    .background(Color.black)
                    .opacity(0.3)
                
                Text("modal")
            }
            .background(BackgroundCleanerView())     // << helper !!
        }

and now helper

struct BackgroundCleanerView: 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) {}
}
like image 186
Asperi Avatar answered Jan 02 '23 06:01

Asperi


The accepted solution works but isn't ideal, as it requires traversing and guessing the view hierarchy.

A more reliable option would be to use UIViewControllerRepresentable instead of UIViewRepresentable, so that the parent controller can be accessed directly.

        .fullScreenCover(isPresented: $present) {
            VStack {
                Text("modal")
            }
            .background(Background()) // << helper !!
        }
struct Background: UIViewControllerRepresentable {
    
    public func makeUIViewController(context: UIViewControllerRepresentableContext<Background>) -> UIViewController {
        return Controller()
    }
    
    public func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<Background>) {
    }
    
    class Controller: UIViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = .clear
        }
        
        override func willMove(toParent parent: UIViewController?) {
            super.willMove(toParent: parent)
            parent?.view?.backgroundColor = .clear
            parent?.modalPresentationStyle = .overCurrentContext
        }
    }
}

like image 41
akaffe Avatar answered Jan 02 '23 07:01

akaffe