I have a SwiftUI Map with MapAnnotations. I would like to have an onTap gesture on the Map, so it deselects the selected annotations, and dissmisses a bottom sheet, etc. Also would like to have an onTap gesture on the annotation item (or just having a button as annotation view with an action there), which selects the annotation and do stuff. The problem: whenever I tap the annotation, the map's ontap gesture is triggered too. (When I tap on the map, it only triggers the map's action, so no problems there.) Here's some sample code:
import SwiftUI
import MapKit
import CoreLocation
struct ContentView: View {
@State var region: MKCoordinateRegion =
MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 47.333,
longitude: 19.222),
span: MKCoordinateSpan(latitudeDelta: 0.002, longitudeDelta: 0.002))
var body: some View {
Map(coordinateRegion: $region,
annotationItems: AnnotationItem.sample) { annotation in
MapAnnotation(coordinate: annotation.location.coordinate) {
VStack {
Circle()
.foregroundColor(.red)
.frame(width: 50)
Text(annotation.name)
}
.onTapGesture {
print(">> tapped child")
}
}
}
.onTapGesture {
print(">> tapped parent")
}
}
}
I tap on the annotation, then:
>> tapped parent
>> tapped child
I tap on the map, then:
>> tapped parent
EDIT:
I have tried and didn't work:
- make parent action depend on a boolean, which is set to prevent map's action when child is tapped. See in comment: I can only delay the parents action with this, cannot cancel it.
- add on custom tap gesture for each, and set .exclusivelyBefore(:) modifier on one of them
Warning: The workaround shown in the edit below works apparently only in special cases, see the comment of Gergely Kovacs below.
This seems to me to be a bug, since the default behavior is that only one gesture recognizer fires at a time, see here.
A similar problem occurs in a
ScrollView, but there exists a property.delaysContentTouchesto solve it, see here. This does unfortunately not exist for aView.A possible workaround is to delay the parent tap action until it is ensured that no child tap action follows. You could add to your
ContentViewa@State var childTapTriggered = falseand set thisvartotrueif it triggered. Then you could use as parent tap gesture closure something likeEDIT (due to the comment of Gergely Kovacs):
The above workaround does not work, sorry, pls see the comment.
But I tested the following workaround, and it works in my case:
I added to the ContentView a state var:
On the annotation view (the child), I have the following modifier:
On the map (the parent) I have the following modifier:
Of course this is again a hack, and the delay to reset
childTappedtofalsehad to be adjusted right.Anyway, maybe this solves your problem!