Reputation:
I'm trying to add a full screen View over my app in SwiftUI. The purpose of this is to have a "shade" that fades in that will darken the screen and bring focus to a custom pop-up, disabling content in the background. See below for a visual example:
The View that I'm trying to add this shade over is embedded in a complex NavigationView stack (several layers deep, accessed via a NavigationLink
) and also has a visible TabBar. So far I've tried embedding the NavigationView
in a ZStack
and adding a Rectangle()
on top but to no avail, the NavigationBar and TabBar still sit on top of this view. I've also tried using using the .zIndex()
modifier but this seems to have done nothing.
Any help is much appreciated,
Thanks
Upvotes: 6
Views: 12778
Reputation: 597
Fairly new to SwiftUI but I was able to implement the above solution using a view modifier
struct CustomPopUpViewModifier<PopupView: View>: ViewModifier {
let popupView: PopupView
let backgroundColor: Color
let animation: Animation?
@Binding var isPresented: Bool
func body(content: Content) -> some View {
content
.animation(nil, value: isPresented)
.overlay(isPresented ? backgroundColor.ignoresSafeArea() : nil)
.overlay(isPresented ? popupView : nil)
.animation(animation, value: isPresented)
}
}
and use it like so:
yourView
.modifier(CustomPopUpViewModifier(popupView: InfoView(isPresented: $showingInfoModal,
viewModel: viewModel),
backgroundColor: .black.opacity(0.5),
animation: .default,
isPresented: $showingInfoModal))
Seems to cover up the navigation bar/items for me
Upvotes: 0
Reputation: 1
You do not need work on zIndex, because you cover the all screen! Even you do not need work on disable your current View for using PopUp, because again PopUp is already on top layer. zIndex would be helpful when you did not cover the screen, here is a way:
struct ContentView: View {
@State private var isPresented: Bool = Bool()
var body: some View {
NavigationView {
VStack {
Button("Show Custom PopUp View") { isPresented.toggle() }
}
.navigationTitle("Navigation Title")
}
.customPopupView(isPresented: $isPresented, popupView: { popupView })
}
var popupView: some View {
RoundedRectangle(cornerRadius: 20.0)
.fill(Color.white)
.frame(width: 300.0, height: 200.0)
.overlay(
Image(systemName: "xmark").resizable().frame(width: 10.0, height: 10.0)
.foregroundColor(Color.black)
.padding(5.0)
.background(Color.red)
.clipShape(Circle())
.padding()
.onTapGesture { isPresented.toggle() }
, alignment: .topLeading)
.overlay(Text("Custom PopUp View!"))
.transition(AnyTransition.scale)
.shadow(radius: 10.0)
}
}
struct CustomPopupView<Content, PopupView>: View where Content: View, PopupView: View {
@Binding var isPresented: Bool
@ViewBuilder let content: () -> Content
@ViewBuilder let popupView: () -> PopupView
let backgroundColor: Color
let animation: Animation?
var body: some View {
content()
.animation(nil, value: isPresented)
.overlay(isPresented ? backgroundColor.ignoresSafeArea() : nil)
.overlay(isPresented ? popupView() : nil)
.animation(animation, value: isPresented)
}
}
extension View {
func customPopupView<PopupView>(isPresented: Binding<Bool>, popupView: @escaping () -> PopupView, backgroundColor: Color = .black.opacity(0.7), animation: Animation? = .default) -> some View where PopupView: View {
return CustomPopupView(isPresented: isPresented, content: { self }, popupView: popupView, backgroundColor: backgroundColor, animation: animation)
}
}
Upvotes: 6
Reputation: 168
This is how I would so it.
struct ContentView: View {
@State var showingShade = false
var body: some View {
ZStack{
// Your other views goes here
if showingShade{
Rectangle()
.ignoresSafeArea()
.foregroundColor(.black)
.opacity(0.5)
}
}
}
}
And then simply set showingShade = true
when you want the shade to appear. It might be a good idea to use the same var as your PopUp.
For disabling the view you can use the .disabled()
modifier on the specific view you want to disable.
Upvotes: 0