Delete Multiple Rows from List in SwiftUI

DevTechie Inc
May 11, 2023

List is one of the most used and convenient view in SwiftUI. Lists combined with ForEach adds a few more cool features to the List views.

Today we will explore multi-row delete functionality for List.

We will start with a list of DevTechie courses in a State array property.

struct DevTechieDeleteFromListExample: View {
    
    @State private var courses = ["Mastering WidgetKit iOS 16", "Practical iOS 16", "New in SwiftUI: Charts", "Complete Machine Learning"]

Next we will add variable for EditMode which is a mode that indicates whether the user can edit a view’s content.

struct DevTechieDeleteFromListExample: View {
    
    @State private var courses = ["Mastering WidgetKit iOS 16", "Practical iOS 16", "New in SwiftUI: Charts", "Complete Machine Learning"]
    @State private var editingMode = EditMode.inactive

We will add one more State property, this one will hold selections from the List view.

struct DevTechieDeleteFromListExample: View {
    
    @State private var courses = ["Mastering WidgetKit iOS 16", "Practical iOS 16", "New in SwiftUI: Charts", "Complete Machine Learning"]
    @State private var editingMode = EditMode.inactive
    @State private var selectedItems = Set<String>()

We will create a private computed properties to build delete and edit buttons.

Edit button: depending upon editing mode for the view, we will show edit or done button on the trailing side of navigation bar.

private var editButton: some View {
        Group {
            if editingMode == .inactive {
                Button(action: {
                    self.editingMode = .active
                    self.selectedItems = Set<String>()
                }) {
                    Text("Edit")
                }
            }
            else {
                Button(action: {
                    self.editingMode = .inactive
                    self.selectedItems = Set<String>()
                }) {
                    Text("Done")
                }
            }
        }
    }

Delete button : We only wanna show delete button when view is in editing mode.

private var deleteButton: some View {
        Group {
            if editingMode == .inactive {
                EmptyView()
            } else {
                Button(action: deleteCourses) {
                    Image(systemName: "trash")
                }
            }
        }
    }

We will also create a private function to delete selected items from the list of items.

private func deleteCourses() {
        for item in selectedItems {
            if let index = courses.lastIndex(where: { $0 == item })  {
                courses.remove(at: index)
            }
        }
        selectedItems = Set<String>()
    }

In the body property, we will add a List inside a NavigationStack and toolbar to add our navigation bar buttons.

var body: some View {
        NavigationStack {
            List(selection: $selectedItems) {
                ForEach(courses, id: \.self) { course in
                    Text(course)
                }
            }
            .navigationTitle("DevTechie Courses")
            .environment(\.editMode, $editingMode)
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    deleteButton
                }
                
                ToolbarItem(placement: .navigationBarTrailing) {
                    editButton
                }
            }
        }
    }

Complete view should look like this:

struct DevTechieDeleteFromListExample: View {
    
    @State private var courses = ["Mastering WidgetKit iOS 16", "Practical iOS 16", "New in SwiftUI: Charts", "Complete Machine Learning"]
    @State private var editingMode = EditMode.inactive
    @State private var selectedItems = Set<String>()
    
    private var deleteButton: some View {
        Group {
            if editingMode == .inactive {
                EmptyView()
            } else {
                Button(action: deleteCourses) {
                    Image(systemName: "trash")
                }
            }
        }
    }
    
    private var editButton: some View {
        Group {
            if editingMode == .inactive {
                Button(action: {
                    self.editingMode = .active
                    self.selectedItems = Set<String>()
                }) {
                    Text("Edit")
                }
            }
            else {
                Button(action: {
                    self.editingMode = .inactive
                    self.selectedItems = Set<String>()
                }) {
                    Text("Done")
                }
            }
        }
    }
    
    var body: some View {
        NavigationStack {
            List(selection: $selectedItems) {
                ForEach(courses, id: \.self) { course in
                    Text(course)
                }
            }
            .navigationTitle("DevTechie Courses")
            .environment(\.editMode, $editingMode)
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    deleteButton
                }
                
                ToolbarItem(placement: .navigationBarTrailing) {
                    editButton
                }
            }
        }
    }
    
    private func deleteCourses() {
        for item in selectedItems {
            if let index = courses.lastIndex(where: { $0 == item })  {
                courses.remove(at: index)
            }
        }
        selectedItems = Set<String>()
    }
}

Build and run