Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to detect a change in the value of “editMode” from a toolbar?

Let’s use a selectable List as follows:

import SwiftUI

struct ContentView: View {
    @State private var elements: Set<UUID> = [.init(), .init(), .init()]
    @State private var selection: Set<UUID> = []
    
    @Environment(\.editMode) private var editMode
        
    var body: some View {
        NavigationStack {
            List(
                elements.sorted(),
                id: \.self,
                selection: $selection,
                rowContent: { Text(String(describing: $0)) }
            )
            .toolbar {
                ToolbarItem(placement: .topBarTrailing, content: {
                    EditButton()
                })
                ToolbarItem(placement: .topBarLeading, content: {
                    Button(selection.count == elements.count ? "Unselect All" : "Select All", action: {
                        selection = selection.count == elements.count ? [] : elements
                    })
                    .opacity(editMode?.wrappedValue.isEditing ?? false ? 1 : 0) // FIXME: `editMode` value change is not detected
                })
            }
        }
    }
}

Although changing the value of editMode correctly updates the UI, it is not detected in the toolbar modifier.

Why ?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Tested in Xcode 16 beta 3.

>Solution :

I’m not sure if this is intentional or not. One possible explanation is that SwiftUI fails to (or is not designed to) find an environment value in ContentView as a dependency of a tool bar button.

This works as expected if you extract the tool bar button into its own View:

struct SelectAllButton: View {
    let elements: Set<UUID>
    @Binding var selection: Set<UUID>
    
    // importantly, read the environment from this extracted view
    @Environment(\.editMode) private var editMode
    
    var body: some View {
        Button(selection.count == elements.count ? "Unselect All" : "Select All", action: {
            selection = selection.count == elements.count ? [] : elements
        })
        .opacity(editMode?.wrappedValue.isEditing ?? false ? 1 : 0)
    }
}
ToolbarItem(placement: .topBarLeading) {
    SelectAllButton(elements: elements, selection: $selection)
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading