iosswiftswiftuimodalviewcontrollerios-navigationview

SwiftUI transition from modal sheet to regular view with Navigation Link


I'm working with SwiftUI and I have a starting page. When a user presses a button on this page, a modal sheet pops up.

In side the modal sheet, I have some code like this:

  NavigationLink(destination: NextView(), tag: 2, selection: $tag) {
            EmptyView()
    }

and my modal sheet view is wrapped inside of a Navigation View.

When the value of tag becomes 2, the view does indeed go to NextView(), but it's also presented as a modal sheet that the user can swipe down from, and I don't want this.

I'd like to transition from a modal sheet to a regular view.

Is this possible? I've tried hiding the navigation bar, etc. but it doesn't seem to make a difference.

Any help with this matter would be appreciated.


Solution

  • You can do this by creating an environmentObject and bind the navigationLink destination value to the environmentObject's value then change the value of the environmentObject in the modal view.

    Here is a code explaining what I mean

    import SwiftUI
    
    class NavigationManager: ObservableObject{
        @Published private(set) var dest: AnyView? = nil
        @Published var isActive: Bool = false
    
        func move(to: AnyView) {
            self.dest = to
            self.isActive = true
        }
    }
    
    struct StackOverflow6: View {
        @State var showModal: Bool = false
        @EnvironmentObject var navigationManager: NavigationManager
        var body: some View {
            NavigationView {
                ZStack {
                    NavigationLink(destination: self.navigationManager.dest, isActive: self.$navigationManager.isActive) {
                        EmptyView()
                    }
    
                    Button(action: {
                        self.showModal.toggle()
                    }) {
                        Text("Show Modal")
                    }
                }
            }
                .sheet(isPresented: self.$showModal) {
                    secondView(isPresented: self.$showModal).environmentObject(self.navigationManager)
                }
        }
    }
    
    struct StackOverflow6_Previews: PreviewProvider {
        static var previews: some View {
            StackOverflow6().environmentObject(NavigationManager())
        }
    }
    
    
    struct secondView: View {
        @EnvironmentObject var navigationManager: NavigationManager
        @Binding var isPresented: Bool
        @State var dest: AnyView? = nil
    
        var body: some View {
            VStack {
                Text("Modal view")
                Button(action: {
                    self.isPresented = false
                    self.dest = AnyView(thirdView())
                }) {
                    Text("Press me to navigate")
                }
            }
            .onDisappear {
                // This code can run any where but I placed it in `.onDisappear` so you can see the animation
                if let dest = self.dest {
                    self.navigationManager.move(to: dest)
                }
            }
        }
    }
    
    struct thirdView: View {
        var body: some View {
            Text("3rd")
                .navigationBarTitle(Text("3rd View"))
        }
    }
    
    

    Hope this helps, if you have any questions regarding this code, please let me know.