Search code examples
listswiftuiforeachrowdelete-row

SwiftUI : How I can delete row of ForEach in VStack?


This is TaskModel

import Foundation
import SwiftUI


struct TaskModel: Identifiable, Codable {
    var id = UUID().uuidString
    var title : String
    var selectedColor : Color
    var remindedTime : Int
    var taskDate : Date
}

This is ViewModel I tried

    import Foundation
    import SwiftUI
    
    class CalendarViewModel : ObservableObject {
        
        @Published var tasks : [TaskModel] = []
        @Published var currentMonth : Int = 0
        
    
        func addTask(title : String, selectedColor : Color, reminderTime : Int, taskDate : Date) {
            let newTask = TaskModel(title: title, selectedColor: selectedColor, remindedTime: reminderTime * 60, taskDate: taskDate)
            tasks.append(newTask)
        }
        
        func deleteTask(task : TaskModel) {
            if let index = tasks.firstIndex(where: {$0.id == task.id }) {
                tasks.remove(at: index)
            }
        }

This is the ForEach I tried to delete row

VStack {
                    ForEach(vm.tasks.filter({vm.isSameDay(date1: $0.taskDate, date2: currentDate)})) { task in
                        TaskRowView(task: task)
                            .actionSheet(isPresented: $isShowActionSheet) {
                                ActionSheet(title: Text("Settings"), message: Text("Press the button that what you want to do 😀"), buttons: [
                                    .cancel(), .destructive(Text("Delete"), action: {
                                        vm.deleteTask(task: task)
                                    }), .default(Text("Edit"), action: {
                                        
                                    })
                                ])
                            }
                    }
                    
                    .onLongPressGesture {
                        isShowActionSheet.toggle()
                    }
                 }

And this is the picture of my app View. enter image description here

The weird thing is when I tried to delete second row of the List, The first row is deleted and second is left.

But, I can't find my miss point.


Solution

  • The simplest way to implement this is with an optional selection variable and a custom binding for the .actionSheet(). Please see the code below. I commented the important parts. I also changed your code to a Minimal Reproducible Example (MRE) for demonstration purposes.

    struct ContentView: View {
        
        @State var tasks = Array(1...20).map( { TaskModel(title: "Task \($0)") })
        // This is your optional selection variable
        @State var selectedRow: TaskModel?
        
        var body: some View {
            VStack {
                ForEach(tasks) { task in
                    Text(task.title)
                        // put the .onLongPressGesture() on the row itself
                        .onLongPressGesture {
                            // set selectedRow to the task
                            selectedRow = task
                        }
                        // This creates a custom binding
                        .actionSheet(isPresented: Binding<Bool>(
                            // the get returns the Bool that is the comparison of selectedRow and task
                            // if they are equal, the .actionSheet() fires
                            get: { selectedRow == task },
                            // when done, .actionSheet() sets the Binding<Bool> to false, but we intercept that
                            // and use it to set the selectedRow back to nil
                            set: { if !$0 {
                                selectedRow = nil
                            }})) {
                            ActionSheet(
                                title: Text("Settings"),
                                // added task.title to prove the correct row is in the ActionSheet
                                message: Text("Press the button for \(task.title)"),
                                buttons: [
                                    .cancel(),
                                    .destructive(Text("Delete"), action: {}),
                                    .default(Text("Edit"), action: {})
                                ]
                            )
                        }
                }
            }
        }
    }