Elisey Ozerov
Elisey Ozerov

Reputation: 230

SwiftUI updating @Binding value in NavigationLink(destination:label) causes destination to reappear

I have a weird issue, where I navigate to EditView and edit a state variable from ContentView. After that I dismiss EditView with presentation.wrappedValue.dismiss(). The problem is, as soon as the view is dismissed, it reappears again.

I'm using XCode 12.4 and my iOS deployment target is set to 14.4

Observations:

    VStack {
        Text("Value: \(title)")
        NavigationLink(destination: EditView(title: $title)){
            Text("Edit")
        }
    }

also resolves the issue, but that seems like a hack. Besides, I'd like to keep using .toolbar because I like the .navigationTitle animation and I can't have a button in the upper-right corner of the screen if I have a navigation title without the toolbar.

Here's the full code:

    import SwiftUI

    struct ContentView: View {
        @State var title: String = "Title"
        @State var isActive: Bool = false
        
        var body: some View {
            NavigationView {
                    
                Text("Value: \(title)")
                    .toolbar {
                        ToolbarItem(placement: .navigationBarTrailing) {
                            NavigationLink(destination: EditView(title: $title, isActive: $isActive), isActive: $isActive){
                                Text("Edit")
                            }
                        }
                    }
            }
        }
    }
    
    struct EditView: View {
        @Environment(\.presentationMode) var presentation
        
        @Binding var title: String
        @Binding var isActive: Bool
        
        var body: some View {
            Button(action: {
                title = "\(Date().timeIntervalSince1970)"
    //            presentation.wrappedValue.dismiss()
                isActive = false
            }){
                Text("Done")
            }
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }

As far as I can tell this is a .toolbar bug, and if it turns out that way I'll report it to Apple, but in the meantime, does anyone have a better solution and/or explanation for this?

Cheers!

EDIT:

I updated the code with isActive value for the NavigationLink. It doesn't work when written like that, but uncommenting the commented out line makes it work. But that's quite hacky.

Upvotes: 0

Views: 2138

Answers (1)

Tushar Sharma
Tushar Sharma

Reputation: 2882

You are mixing up 2 things NavigationLink will push the view on stack, just like NavigationController in swift. That’s why you can see back button after navigating to second view. When you hit back button, it will pop top most view out of stack. presentationMode is not needed , dismissing presented view will not pop it of the stack.

To present a view and dismiss it you can check below code.

import SwiftUI

struct ContentViewsss: View {
    @State var title: String = "Title"
    @State var isPresented = false
    
    var body: some View {
        NavigationView {
            Text("Value: \(title)")
                .toolbar {
                    ToolbarItem(placement: .navigationBarTrailing) {
                        Button(action: {
                            isPresented.toggle()
                        }){
                            Text("Edit")
                        }
                    }
                }
        }.sheet(isPresented: $isPresented, content: {
            EditView(title: $title, state: $isPresented)
        })
    }
}

struct EditView: View {
   
    @Binding var title: String
    @Binding var state: Bool
    
    var body: some View {
        Button(action: {
            title = "\(Date().timeIntervalSince1970)"
            state.toggle()
        }){
            Text("Done")
        }
    }
}

If you want NavigationLink functionality, you can just remove presentationMode code from second view, and keep ContentView as it is -:

struct EditView: View {
     //@Environment(\.presentationMode) var presentation
    
    @Binding var title: String
      
    var body: some View {
        Button(action: {
            title = "\(Date().timeIntervalSince1970)"
            // presentation.wrappedValue.dismiss()
        }){
            Text("Done")
        }
    }
}

Upvotes: 1

Related Questions