In code below, tapping on Annotation displays an Alert with two buttons, "Cancel" and "Buy".
Tapping the Annotation or the Alert "Buy" button also prints some information to console, but the "Buy" button doesn't always print the correct information.
For example, tapping the Annotation will print "Toyota Camry 2001". Tapping Alert "Buy" button sometimes prints "Ford Mustang 2000" instead, sometimes prints "Toyota Camry 2001".
Same behaviour in 17.5 and 18. Trying to understand why or if it's a bug?
import SwiftUI
import MapKit
struct Car: Hashable {
let make: String
let model: String
let year: Int
let lat: Double
let lng: Double
}
struct ContentView: View {
let cars: [Car] = [
.init(make: "Ford", model: "Mustang", year: 2000, lat: 40.7117, lng: -74.0049),
.init(make: "Toyota", model: "Camry", year: 2001, lat: 40.7127, lng: -74.0059)
]
@State private var position = MapCameraPosition.automatic
@State private var showAlert = false
var body: some View {
Map(position: $position) {
ForEach(cars, id: \.self) { car in
Annotation(coordinate: CLLocationCoordinate2D(latitude: car.lat, longitude: car.lng)) {
Image(systemName: "mappin.circle.fill")
.font(.system(size: 50))
.onTapGesture {
showAlert.toggle()
print(car.make, car.model, car.year)
}
.alert(
"Question",
isPresented: $showAlert
) {
HStack {
Button("Cancel", role: .cancel) {
// do nothing
}
Button("Buy") {
print(car.make, car.model, car.year)
}
}
} message: {
Text("Do you want to buy this car?")
}
}
label: {
Text(car.model)
}
}
}
.mapStyle(.standard(elevation: .automatic))
}
}
#Preview {
ContentView()
}
Try this approach using struct Car: Identifiable,...
with a ForEach(cars){...}
and moving the .alert
outside the ForEach
loop using the support of selectedCar
,
as shown in the example code:
Note, the ForEach
loop must have unique elements, here supplied by the Identifiable
,
do not use ForEach(cars, id: \.self)
. Also the .alert
should not be in the ForEach
loop. With those changes the code will show the correct car info.
struct Car: Identifiable, Hashable { // <--- here
let id = UUID() // <--- here
let make: String
let model: String
let year: Int
let lat: Double
let lng: Double
}
struct ContentView: View {
let cars: [Car] = [
.init(make: "Ford", model: "Mustang", year: 2000, lat: 40.7117, lng: -74.0049),
.init(make: "Toyota", model: "Camry", year: 2001, lat: 40.7127, lng: -74.0059)
]
@State private var position = MapCameraPosition.automatic
@State private var showAlert = false
@State private var selectedCar: Car? // <--- here
var body: some View {
Map(position: $position) {
ForEach(cars) { car in // <--- here
Annotation(coordinate: CLLocationCoordinate2D(latitude: car.lat, longitude: car.lng)) {
Image(systemName: "mappin.circle.fill")
.font(.system(size: 50))
.onTapGesture {
selectedCar = car // <--- here
print(car.make, car.model, car.year)
showAlert.toggle()
}
}
label: {
Text(car.model)
}
}
}
.mapStyle(.standard(elevation: .automatic))
// --- here
.alert(
"Question",
isPresented: $showAlert,
presenting: selectedCar
) { car in
HStack {
Button("Cancel", role: .cancel) {
// do nothing
}
Button("Buy") {
print("--> buy ", car.make, car.model, car.year)
}
}
} message: { _ in
Text("Do you want to buy this car?")
}
}
}