drago
drago

Reputation: 1283

Transparent and Blurred fullScreenCover modal in SwiftUI?

I'm the fullScreenCover to show a modal in my SwiftUI project.

However, I need to show the modal with a transparent and blurred background.

But I can't seem to be able to achieve this at all.

This is my code:

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

And I have this on the same View:

struct BackgroundBlurView: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        let view = UIVisualEffectView(effect: UIBlurEffect(style: .light))
        DispatchQueue.main.async {
            view.superview?.superview?.backgroundColor = .clear
        }
        return view
    }

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

The above code will open the fullscreen modal but its not transparent or blurred at all.

This is what I get:

enter image description here

is there something else I need to do?

Reference to where I got the above code from:

SwiftUI: Translucent background for fullScreenCover

Upvotes: 3

Views: 5576

Answers (3)

Aditya Vyavahare
Aditya Vyavahare

Reputation: 53

Well there's an inbuilt view modifier in SwiftUI Below is a sample

MainView()
    .blur(radius: <condition, if any> ? 10 : 0)
    .animation(.easeInOut, value: <conditional value>) // for blurring effect

This worked like a charm in my case

Upvotes: 0

Raja Kishan
Raja Kishan

Reputation: 18914

@Asperi answer from here is totally correct. Just need to add alpha to blur view.

struct BackgroundBlurView: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        let view = UIVisualEffectView(effect: UIBlurEffect(style: .light))
        view.alpha = 0.5 //< --- here
        DispatchQueue.main.async {
            view.superview?.superview?.backgroundColor = .clear
        }
        return view
    }
    
    func updateUIView(_ uiView: UIView, context: Context) {}
}

Another approach is to use ViewControllerHolder reference link

struct ViewControllerHolder {
    weak var value: UIViewController?
}

struct ViewControllerKey: EnvironmentKey {
    static var defaultValue: ViewControllerHolder {
        return ViewControllerHolder(value: UIApplication.shared.windows.first?.rootViewController)
    }
}

extension EnvironmentValues {
    var viewController: UIViewController? {
        get { return self[ViewControllerKey.self].value }
        set { self[ViewControllerKey.self].value = newValue }
    }
}

extension UIViewController {
    func present<Content: View>(backgroundColor: UIColor = UIColor.clear, @ViewBuilder builder: () -> Content) {
        let toPresent = UIHostingController(rootView: AnyView(EmptyView()))
        toPresent.view.backgroundColor = backgroundColor
        toPresent.modalPresentationStyle = .overCurrentContext
        toPresent.modalTransitionStyle = .coverVertical
        toPresent.rootView = AnyView(
            builder()
                .environment(\.viewController, toPresent)
        )
        
        //Add blur efect
        let blurEfect = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
        blurEfect.frame = UIScreen.main.bounds
        blurEfect.alpha = 0.5
        toPresent.view.insertSubview(blurEfect, at: 0)
        self.present(toPresent, animated: true, completion: nil)
    }
}
struct ContentViewNeo: View {
    @Environment(\.viewController) private var viewControllerHolder: UIViewController?
    
    var body: some View {
        VStack {
            Text("Background screen")
            Button("Open") {
                viewControllerHolder?.present(builder: {
                    SheetView()
                })
            }
        }
    }
}

struct SheetView: View {
    var body: some View {
        Text("Sheet view")
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
    }
}

Upvotes: 5

jnpdx
jnpdx

Reputation: 52387

I removed some extraneous stuff and made sure that there was content in the background that you could see peeking through.

Without the alpha that is suggested in a different answer, the effect is very subtle, but there.

struct ContentView : View {
    @State var isPresented = false
    
    var body: some View {
        VStack {
            Text("Some text")
            Spacer()
            Text("More")
            Spacer()
            Button(action: {
                isPresented.toggle()
            }) {
                Text("Show fullscreenview")
            }
            .fullScreenCover(isPresented: $isPresented) {
                Text("modal")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .background(BackgroundBlurView())
            }
            Spacer()
            Text("More text")
        }
        
    }
}

struct BackgroundBlurView: UIViewRepresentable {
    func makeUIView(context: Context) -> UIView {
        let view = UIVisualEffectView(effect: UIBlurEffect(style: .light))
        DispatchQueue.main.async {
            view.superview?.superview?.backgroundColor = .clear
        }
        return view
    }

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

Note the extremely light blue you can see shining through

Note the extremely subtle blue you can see from the Button shining through. More obvious in dark mode.

enter image description here

The effect may get more dramatic as you add more color and content to the background view. Adding alpha certainly makes things more obvious, though.

Upvotes: 5

Related Questions