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:
What are my options?
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:
VisualEffectView
structThis 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
}
}
VisualEffectView
in Your SwiftUI ViewYou 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
}
}
}
}