How it works
EditMode is an enumeration that represents whether a view hierarchy is currently being edited, with cases inactive and transient for normal display and active for the editing state. SwiftUI propagates it down the view tree through the environment, where container views like List read it to decide whether to show delete badges, reordering grips, and selection controls. Reach for EditMode when you want a view's editing affordances to turn on and off together, and when you need your own controls to respond to that same state rather than tracking edit status by hand.
Read the editing state from the environment
EditModelives in the environment under theeditModekey, so any view can observe it with@Environment(\.editMode). AListconsults this value automatically: when it becomesactive, the rows in theForEachoverfruitsreveal their editing controls; when it returns toinactive, they hide again.Toggle the mode with EditButton
EditButtonis the built-in control that flips the environment'sEditModebetweenactiveandinactivefor you. Placing it in the.toolbargives theLista standard Edit/Done button without any state of your own, since the button writes directly into the sameeditModebinding theListreads.Enable row removal in the active state
Attaching
.onDeleteto theForEachregisters a deletion handler that theListonly surfaces whileEditModeisactive. Here the closure callsfruits.remove(atOffsets:), so the delete controls that appear in edit mode actually mutate the backing data.Enable reordering in the active state
Similarly,
.onMovesupplies a reorder handler that becomes reachable through the drag grips shown whenEditModeisactive. The closure forwards tofruits.move(fromOffsets:toOffset:), letting the user rearrange rows while editing.Drive your own controls from the same mode
Because
EditModeis a plainEquatableenum carried in the environment, you can branch on it yourself, for example checkingeditMode?.wrappedValue == .activeto show extra UI alongside theList. This keeps custom affordances in sync with the same state thatEditButtontoggles and the rows respond to.
@Environment(\.editMode) private var editMode to EditModeDemo and place a Text(editMode?.wrappedValue == .active ? "Editing" : "Viewing") above the List to watch the value flip as you tap the Edit button.Example & preview
Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.
struct EditModeDemo: View {
@State private var fruits = ["Apple", "Banana", "Cherry"]
var body: some View {
NavigationStack {
List {
ForEach(fruits, id: \.self) { fruit in
Text(fruit)
}
.onDelete { fruits.remove(atOffsets: $0) }
.onMove { fruits.move(fromOffsets: $0, toOffset: $1) }
}
.navigationTitle("Fruits")
.toolbar { EditButton() }
}
}
}