Mehul Mandalia
Mehul Mandalia

Reputation: 33

Dismissing a View using a "static nav bar"

I've created a simple view acting as a navbar which contains a menu button and some text. I'm using this as a top-level element outside my NavigationView which allows me to have the view static across all child pages that come into view. The reason I'm trying not to use the default navbar, with navbar items, is to avoid the dismissal/creation that you get along with the fading animation when you switch views.

The problem I'm now facing is dismissing the child view's when I have navigated away from the parent view. I'm able to update the button from a menu icon to a back icon, but the action of the button is not triggered. Been looking online to see if anyone has done something similar but had no luck, I'm not sure what I'm trying to achieve is even possible or whether I am going about it the right way. Is there anyway to call self.presentationMode.wrappedValue.dismiss() from the child views even though the header is initialised in the root view? Any help is appreciated, here's what I have so far:

Root View (View1):

struct View1: View {
    @State var showMenuButton: Bool = false
    var body: some View {
        VStack {
            CustomNavigationView(showMenuButton: self.showMenuButton)

            NavigationView {
                NavigationLink(destination: View2()) {
                    Text("View 2")
                }
                .navigationBarTitle("")
                .navigationBarHidden(true)
                .onDisappear(){
                    self.showMenuButton = false
                }
                .onAppear() {
                    self.showMenuButton = true
                }
            }
        }
    }
}

Child View of root view (View2):

struct View2: View {
    var body: some View {
        VStack{
            Text("This is View 2")
            .navigationBarTitle("")
            .navigationBarHidden(true)
            NavigationLink(destination: View3()) {
                Text("View 3")
            }
        }
    }
}

Child view of view 2 (View3):

struct View3: View {
    var body: some View {
        VStack{
            Text("This is View 3")
            .navigationBarTitle("")
            .navigationBarHidden(true)
        }
    }
}

Custom Navigation View:

struct CustomNavigationView: View {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    var showMenuButton = false
    var body: some View {
        VStack {
            HStack {
                if showMenuButton {
                    Button(action: {
                        //Do Something
                    }) {
                        Image(systemName: "line.horizontal.3")
                        .foregroundColor(.black)
                    }
                } else {
                    Button(action: { self.presentationMode.wrappedValue.dismiss()}) {
                        Image(systemName: "arrow.left")
                        .foregroundColor(.black)
                    }
                }
                Text("Sometext")            
            }
        }
    }
}

Upvotes: 0

Views: 95

Answers (1)

Aye Chan
Aye Chan

Reputation: 1427

The environment object 'presentationMode' that you used inside the first view cannot dismiss the views you pushed. Every view that wants to be dismissed must have their own objects. The object inside the first view does not belong to any other pushed views. So, you need to create view model to manage this task.

Here is the example code. Hope that will help you solve your problem.

class NavigationObserver: ObservableObject {
    private var views: [Int:Binding<PresentationMode>] = [:]
    private var current: Int = 0

    func popView() {
       guard let view = views[current] else {
          return
       }
       view.wrappedValue.dismiss()
       views[current] = nil
       current -= 1
     }

    func pushView(id: Int, newView: Binding<PresentationMode>) {
       guard views[id] == nil else {
          return
       }
       current += 1
       views[id] = newView
     }
   }

struct ContentView: View {
@State var showMenuButton: Bool = false
@ObservedObject var observer = NavigationObserver()
var body: some View {
    VStack {
        CustomNavigationView(observer: self.observer, showMenuButton: self.showMenuButton)

        NavigationView {
            NavigationLink(destination: View2(observer: self.observer)) {
                Text("View 2")
            }
            .navigationBarTitle("")
            .navigationBarHidden(true)
            .onDisappear(){
                self.showMenuButton = false
            }
            .onAppear() {
                self.showMenuButton = true
            }
        }
    }
}}
struct View2: View {
@Environment(\.presentationMode) var presentationMode
@ObservedObject var observer: NavigationObserver
var body: some View {
    VStack{
        Text("This is View 2")
        .navigationBarTitle("")
        .navigationBarHidden(true)
        NavigationLink(destination: View3(observer: self.observer)) {
            Text("View 3")
        }
    }.onAppear {
        self.observer.pushView(id: 1, newView: self.presentationMode)
    }
}}

struct View3: View {
@Environment(\.presentationMode) var presentationMode
@ObservedObject var observer: NavigationObserver
var body: some View {
    VStack{
        Text("This is View 3")
        .navigationBarTitle("")
        .navigationBarHidden(true)
    }.onAppear
        {
            self.observer.pushView(id: 2, newView: self.presentationMode)
        }
}
}

struct CustomNavigationView: View {
@ObservedObject var observer: NavigationObserver
var showMenuButton = false
var body: some View {
    VStack {
        HStack {
            if showMenuButton {
                Button(action: {
                    //Do Something
                }) {
                    Image(systemName: "line.horizontal.3")
                    .foregroundColor(.black)
                }
            } else {
                Button(action: {
                    self.observer.popView()
                }) {
                    Image(systemName: "arrow.left")
                    .foregroundColor(.black)
                }
            }
            Text("Sometext")
        }
    }
}
}

Thanks, X_X

Upvotes: 0

Related Questions