Search code examples
swiftuiswiftui-listswiftui-toolbar

How to hide a swiftui ToolBarItem when List is in edit mode?


I have a simple List inside a navigation stack with a toolbar. The toolbar contains an edit button and a "Finished" button to dismiss the view. How do I hide the "Finished" button when the view is in edit mode?

In the code below, I'm trying to use the environment variable editMode. This does not currently work correctly. It correctly hides the "Finished" button when I first press "edit" but it does not correctly reshow the button when I press "done". Also, pressing "edit" does not immediately switch to edit mode--it now takes two taps on the button to enter edit mode.

How do I correct this so that "done" returns when not editing and so that I only need press edit once to enter edit mode?

import SwiftUI

struct TestView: View {
    @Environment(\.dismiss) var dismiss
    @Environment(\.editMode) private var editMode
    @State var tests:Array<String> = ["A","B","C"]
    
    var body: some View {
        NavigationStack{
            List{
                Section("Recent"){
                    ForEach(tests, id:\.self){ test in
                        Text(test)
                    }
                    .onDelete{ indexes in
                        tests.remove(atOffsets: indexes)
                    }
                    .onMove{ source, destination in
                        tests.move(fromOffsets: source, toOffset: destination)
                    }
                    
                }
            }
                .navigationTitle("Test")
                .toolbar{
                    ToolbarItem(placement: .navigationBarLeading){
                        EditButton()
                    }
                    if editMode?.wrappedValue.isEditing == false{
                        ToolbarItem(placement: .navigationBarTrailing){
                            Button("Finished"){
                                dismiss()
                            }
                            
                        }
                    }
                }
        }

    }
}

#Preview {
    TestView()
}


Solution

  • To ... hide the "Finished" button when the view is in edit mode..., you could try this approach, where you declare a isEditing var with a simultaneousGesture to toggle it and set the .environment(\.editMode,...) accordingly, as shown in the example code:

    struct TestView: View {
        @Environment(\.dismiss) var dismiss
        @Environment(\.editMode) private var editMode
        @State var tests:Array<String> = ["A","B","C"]
        
        @State private var isEditing = false  // <-- here
        
        var body: some View {
            NavigationStack{
                List{
                    Section("Recent"){
                        ForEach(tests, id:\.self){ test in
                            Text(test)
                        }
                        .onDelete{ indexes in
                            tests.remove(atOffsets: indexes)
                        }
                        .onMove{ source, destination in
                            tests.move(fromOffsets: source, toOffset: destination)
                        }
                    }
                }
                .navigationTitle("Test")
                .toolbar {
                    ToolbarItem(placement: .navigationBarLeading) {
                        EditButton()
                            .simultaneousGesture(TapGesture().onEnded {  // <-- here
                                isEditing.toggle()
                            })
                    }
                    if !isEditing {  // <-- here
                        ToolbarItem(placement: .navigationBarTrailing) {
                            Button("Finished"){
                                dismiss()
                            }
                        }
                    }
                }
                .environment(\.editMode, .constant(isEditing ? .active : .inactive))  // <-- here
            }
        }
            
    }