Search code examples
iosswiftswiftuicontextmenu

Inconsistent ContextMenu forward selection


I am trying to build a ContextMenu to allow the app user to Edit or Remove existing workouts from its collection when long pressing one of the workouts, but somehow to Workout selection is not consistent between Edit and Remove actions.

let workouts = workoutManager.getAllWorkoutsFromCollection()
if workouts.count > 0 {
    ForEach(workouts) { workout in
        WorkoutCardView(workout: workout)
            .contextMenu {
                Button {
                    print("Edit Workout: \(workout.title)")     // for debug purpose
                    
                    showEditWorkoutSheet.toggle()
                } label: {
                    Label("Edit Workout", systemImage: "square.and.pencil")
                }
                
                Button(role: .destructive) {
                    print("Delete Workout: \(workout.title)")   // for debug purpose
                    
                    workoutManager.removeWorkoutFromCollection(workout)
                } label: {
                    Label("Delete Workout", systemImage: "trash")
                }
            }
            .sheet(isPresented: $showEditWorkoutSheet) {
                AWSheetView(workout: workout, editWorkout: true)
            }
    }
} 

workoutManager.getAllWorkoutsFromCollection() returns all workouts as an array of Workout custom data type.

func removeWorkoutFromCollection(_ workout: Workout) {
    print("WorkoutManager::removeWorkoutFromCollection: \(workout.title)")  // for  debug purpose
    
    workouts.removeAll(where: { $0.id == workout.id } )
}

func updateWorkout(_ workout: Workout) {
    if let index = workouts.firstIndex(where: { $0.id == workout.id } ) {
        print("WorkoutManager::updateWorkout: \(workout.title)")            // for  debug purpose
        
        workouts[index] = workout
    }
}

Inside AWSheetView I have the followings:

@State var workout: Workout = Workout()
var editWorkout: Bool = false

I am also building an empty Workout here because I am using AWSheetView to add a new Workout to the collection but also to edit an existing workout based on editWorkout variable.

When long pressing to a Workout and selecting Delete, the selected workout gets deleted from collection and I have the following console output:

Delete Workout: Legs Day
WorkoutManager::removeWorkoutFromCollection: Legs Day

but when pressing Edit the output looks like below and somehow always the first workout is received in AWSheetView instead of the selected one:

Edit Workout: Legs Day
WorkoutManager::updateWorkout: Upper body

Does anyone knows what am I doing wrong here? I am trying for severals days to solve it but no results.

Below are 2 screenshots. In the second screenshot you can see how AWSheetView looks like when pressing Edit on "Legs day" workout.

Legs day long press:

The result when pressing Edit on "Legs day" workout:


Solution

  • The solution pointed out by Asperi (check here the solution) works very well.

    This is new implementation based on the above link:

    @State private var selectedWorkout: Workout? = nil
    
    let workouts = workoutManager.getAllWorkoutsFromCollection()
    if workouts.count > 0 {
        ForEach(workouts) { workout in
            WorkoutCardView(workout: workout)
                .contextMenu {
                    Button {
                        print("Edit Workout: \(workout.title)")     // for debug purpose
                        
                        selectedWorkout = workout
                    } label: {
                        Label("Edit Workout", systemImage: "square.and.pencil")
                    }
                    
                    Button(role: .destructive) {
                        print("Delete Workout: \(workout.title)")   // for debug purpose
                        
                        workoutManager.removeWorkoutFromCollection(workout)
                    } label: {
                        Label("Delete Workout", systemImage: "trash")
                    }
                }
        }
        .sheet(item: self.$selectedWorkout) { selectedWorkout in
            AWSheetView(workout: selectedWorkout, editWorkout: true)
        }
    } else {
        Text("No workouts found.")
            .font(.system(size: 16))
            .foregroundColor(.red)
    }