Wesley de Groot's Blog
Building Editable Lists in SwiftUI

Back

In this post, we will explore how to create editable lists in SwiftUI using a couple of different approaches using EditButton(), @Environment(\.editMode) and custom EditMode state.

What is List, EditButton(), EditMode and @Environment(\.editMode)?

List is a SwiftUI view that presents a scrollable list of rows. It is commonly used to display collections of data in a structured way.

The EditButton() is a built-in SwiftUI button that toggles the edit mode of a view. When tapped, it switches the view between editing and non-editing states, please note this will only update the List's edit mode, it cannot be read from somewhere else in the view.

The EditMode is an enumeration that represents the different states of a view's edit mode. It has two cases we want to use .inactive and .active.

The @Environment(\.editMode) property wrapper allows you to access the current edit mode of the view, enabling you to create editable lists.

Example List with EditButton()

To create an editable list, you can use the List view in combination with EditButton().

struct Pokemon: Identifiable {
    let id: UUID
    let name: String

    init(name: String) {
        self.id = UUID()
        self.name = name
    }
}

struct EditModeView: View {
    @State private var pokemon: [Pokemon] = [
        .init(name: "Dragonite"),
        .init(name: "Lugia"),
        .init(name: "Pikachu"),
        .init(name: "Mewtwo")
    ]

    @State private var selectedIems: Set<UUID> = []

    var body: some View {
        NavigationStack {
            List(selection: $selectedIems) {
                ForEach(pokemon) { pokemon in
                    Text(pokemon.name)
                }
                .onDelete { pokemon.remove(atOffsets: $0) }
                .onMove { pokemon.move(fromOffsets: $0, toOffset: $1) }
            }
            .toolbar {
                ToolbarItem(placement: .primaryAction) {
                    EditButton()
                }
            }
        }
    }
}

#Preview {
    EditModeView()
}

Example List and @Environment(\.editMode)

To create an editable List using @Environment(\.editMode), you can follow this example:

struct EditModeView: View {
    @Environment(\.editMode) private var editMode
    @State private var name = "Firstname Lastname"

    var body: some View {
        NavigationStack {
            Form {
                if editMode?.wrappedValue.isEditing == true {
                    TextField("Name", text: $name)
                } else {
                    Text(name)
                }
            }
            .animation(nil, value: editMode?.wrappedValue)
            .toolbar {
                EditButton()
            }
        }
    }
}

#Preview {
    EditModeView()
}

Example List with custom EditMode

To create an editable List using a custom EditMode state, you can follow this example:

import SwiftUI

struct Pokemon: Identifiable {
    let id: UUID
    let name: String

    init(name: String) {
        self.id = UUID()
        self.name = name
    }
}

struct EditModeView: View {
    // Custom edit mode.
    @State var editMode: EditMode = .inactive

    @State private var pokemon: [Pokemon] = [
        .init(name: "Dragonite"),
        .init(name: "Lugia"),
        .init(name: "Pikachu"),
        .init(name: "Mewtwo")
    ]

    @State private var selectedIems: Set<UUID> = []

    var body: some View {
        NavigationStack {
            List(selection: $selectedIems) {
                ForEach(pokemon) { pokemon in
                    Text(pokemon.name)
                }
                .onDelete { pokemon.remove(atOffsets: $0) }
                .onMove { pokemon.move(fromOffsets: $0, toOffset: $1) }
            }
            // Tell the List that we are if editting
            .environment(\.editMode, $editMode)
            .toolbar {
                ToolbarItem(placement: .primaryAction) {
                    // Toggle editmode
                    Button(editMode.isEditing ? "Done" : "Edit") {
                        withAnimation {
                            editMode = editMode.isEditing ? .inactive : .active
                        }
                    }
                }

                ToolbarItem(placement: .bottomBar) {
                    VStack {
                        Text("Selected: \(selectedIems.count), edit mode: \(editMode.isEditing)")

                        HStack {
                            if selectedIems.isEmpty {
                                Button("Select All") {
                                    selectedIems.formUnion(pokemon.map(\.id))
                                    editMode = .active
                                }
                            } else {
                                Button("Deselect All") {
                                    selectedIems.removeAll()
                                    editMode = .inactive
                                }
                            }

                            Spacer()

                            Button("Delete") {
                                pokemon.removeAll(where: {
                                    selectedIems.contains($0.id)
                                })
                                selectedIems = []
                                editMode = .inactive
                            }
                            .disabled(selectedIems.isEmpty)
                            .disabled(!editMode.isEditing)
                            .frame(maxWidth: .infinity)
                        }
                    }
                    .frame(maxWidth: .infinity)
                }
            }
        }
    }
}

#Preview {
    EditModeView()
}

Caveats

@Environment(\.editMode) only passes the EditMode to the List/Form and will not be updated anywhere else, that's why you need to manage the edit mode state manually if you want to support "Select All", "Deselect All", and "Delete" as a button.

Wrap up

In this article, we explored how to create editable lists in SwiftUI using a custom edit mode. We covered the basics of managing selection and deletion of items, as well as how to toggle edit mode. With these techniques, you can create more interactive and user-friendly list interfaces in your SwiftUI applications.

Resources:

Read more

Share


Share Bluesky Mastodon Twitter LinkedIn Facebook
x-twitter mastodon github linkedin discord threads instagram whatsapp bluesky square-rss sitemap