3

Given a List view with an EditButton() in there, if the view is being edited (user tapped the Edit button), how can one disable the edit mode programmatically when the user navigates away from the view? (e.g. similar behavior to the Favorites view in the Phone app on the iPhone)

struct ContentView2: View {
    @State var fruits = ["Apple", "Banana"]

    var body: some View {
        NavigationView {
            List {
                ForEach(fruits, id: \.self) { fruit in
                    Text(fruit)
                }
                .onDelete { _ in
                    print("Delete")
                }
            }
            .toolbar {
                EditButton()
            }
        }
        .onDisappear {
            // make List's editMode be .inactive; how?
        }
    }
}
George
  • 25,988
  • 10
  • 79
  • 133
vmallet
  • 479
  • 3
  • 11

1 Answers1

6

You can use .environment to bind the List's current edit mode with a @State property, as shown in this awesome article.

struct ContentView: View {
    @State var editMode = EditMode.inactive /// the edit mode

    @State var fruits = ["Apple", "Banana"]
    
    var body: some View {
        NavigationView {
            List {
                ForEach(fruits, id: \.self) { fruit in
                    Text(fruit)
                }
                .onDelete { _ in
                    print("Delete")
                }
            }
            .toolbar {
                EditButton()
            }
            .environment(\.editMode, $editMode) /// bind it here!
        }
        .onDisappear {
            editMode = .inactive /// set to inactive
        }
    }
}

Also, your onDisappear won't be called if you're using a NavigationLink to present a new view, because the NavigationView that you attached it to won't actually disappear. You should attach it to the NavigationLink's label instead (from this answer).

Final code:

struct ContentView: View {
    @State var editMode = EditMode.inactive /// the edit mode
    @State var fruits = ["Apple", "Banana"]
    
    var body: some View {
        NavigationView {
            VStack {
                NavigationLink(destination:  Text("New view!")) {
                    Text("Navigate away")
                        .onDisappear {
                            editMode = .inactive /// set to inactive
                        }
                }
                
                List {
                    ForEach(fruits, id: \.self) { fruit in
                        Text(fruit)
                    }
                    .onDelete { _ in
                        print("Delete")
                    }
                }
                .toolbar {
                    EditButton()
                }
                .environment(\.editMode, $editMode) /// bind it here!
            }
        }
    }
}

Result:

Without editMode = .inactive With editMode = .inactive
List stays in edit mode after presenting new view and coming back List exits edit mode after presenting new view and coming back
aheze
  • 24,434
  • 8
  • 68
  • 125
  • 2
    Excellent, thanks. It turns out I had tried something similar but I had added the `.environment(\.editMode, $editMode)` _before_ the toolbar/button which broke the button. Doing the binding after adding the button is the key to happiness. Good to know for future readers. – vmallet Apr 25 '21 at 22:07
  • @vmallet yep! SwiftUI modifier ordering is very specific – aheze Apr 25 '21 at 22:12