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 ?
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)
}