user5472637
user5472637

Reputation:

SwiftUI: Full screen View over NavigationBar and TabBar

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:

What I've gotWhat I want

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

Answers (3)

rmigneco
rmigneco

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

swiftPunk
swiftPunk

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)
    }
}

enter image description here

Upvotes: 6

federicoramos77
federicoramos77

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

Related Questions