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