Search code examples
swiftswiftuialertlazyvgrid

Alert is taking wrong values inside LazyVGrid SwiftUI


Alert should remove objects from a model, but when Alert is apearing, values are not the same as a value inside tap and longPress Gesture. Any idea why?

LazyVGrid(columns: columns, spacing: 5) {
            ForEach(model.robotsRepository.robots) { robot in
                RobotView(model: RobotView.Model(robotsRepository: model.robotsRepository, robot: robot)).environmentObject(team)
                    .contentShape(Rectangle())
                    .alert(isPresented: $showAlert) {
                        //When Alert is apearing, "robot" value are not the same as a value inside tap and longPress Gesture
                        Alert(
                            title: Text("Remove Card"),
                            message: Text("Are you sure you want to remove this card?"),
                            primaryButton: .destructive(Text("Remove")) {
                                withAnimation {
                                    model.remove(robot: robot)
                                }
                            },
                            secondaryButton: .cancel() {
                                withAnimation {
                                    deleting = false
                                }
                            }
                        )
                    }
                    .onTapGesture {
                        addRobot(robot: robot, at: selectedIndex)
                    }
                    .onLongPressGesture {
                        withAnimation {
                            showAlert = true
                            deleting = true
                        }
                    }
            }
        }

Solution

  • you could try moving the .alert(isPresented: $showAlert) {...} out of the ForEach, and probably out of the LazyVGrid as well. Something like this, for example (untested):

    @State var selectedRobot = Robot.default  // <--- here adjust accordingly
    
    LazyVGrid(columns: columns, spacing: 5) {
                ForEach(model.robotsRepository.robots) { robot in
                    RobotView(model: RobotView.Model(robotsRepository: model.robotsRepository, robot: robot)).environmentObject(team)
                        .contentShape(Rectangle())
                        .onTapGesture {
                            addRobot(robot: robot, at: selectedIndex)
                        }
                        .onLongPressGesture {
                            withAnimation {
                                selectedRobot = robot  // <-- here
                                showAlert = true
                                deleting = true
                            }
                        }
                }
            }
            .alert(isPresented: $showAlert) {
                Alert(
                    title: Text("Remove Card"),
                    message: Text("Are you sure you want to remove this card?"),
                    primaryButton: .destructive(Text("Remove")) {
                        withAnimation {
                            model.remove(robot: selectedRobot) // <-- here
                        }
                    },
                    secondaryButton: .cancel() {
                        withAnimation {
                            deleting = false
                        }
                    }
                )
            }
         
    

    EDIT-1: Note that alert(isPresented:content:) and alert(item:content:) are deprecated (iOS 13.0–15.4 ).

    If you are using (ios15+) you can use the following approach (as per the Apple example: https://developer.apple.com/documentation/swiftui/view/alert(_:ispresented:presenting:actions:message:)-8584l)

    // for demonstration
    struct Robot: Identifiable {
        let id = UUID()
        let name: String = "no name"
    }
    
    .....
    
    @State var selectedRobot: Robot?  // <--- here
    
     .....
    
            LazyVGrid(columns: columns, spacing: 5) {
                ForEach(model.robotsRepository.robots) { robot in
                    RobotView(model: RobotView.Model(robotsRepository: model.robotsRepository, robot: robot)).environmentObject(team)
                        .contentShape(Rectangle())
                        .onTapGesture {
                            addRobot(robot: robot, at: selectedIndex)
                        }
                        .onLongPressGesture {
                            withAnimation {
                                selectedRobot = robot  // <-- here
                                showAlert = true
                                deleting = true
                            }
                        }
                }
            }
            .alert("Remove Card", isPresented: $showAlert, presenting: selectedRobot) { robot in
                Button(role: .destructive) {
                    withAnimation {
                        model.remove(robot: robot) 
                    }
                } label: { Text("Remove") }
                
                Button(role: .cancel) {
                    withAnimation {
                        deleting = false
                    }
                } label: { Text("Cancel") }
                
            } message: { _ in
                Text("Are you sure you want to remove this card?")
            }