我有一个带有 MapAnnotations 的 SwiftUI 地图。 我想在地图上有一个 onTap 手势,这样它就会取消选择选定的注释,并取消底部工作表等。还想在注释项上有一个 onTap 手势(或者只是有一个按钮作为注释视图,并带有那里的操作),它选择注释并执行操作。 问题:每当我点击注释时,地图的 ontap 手势也会被触发。 (当我点击地图时,它只会触发地图的操作,所以没有问题。) 这是一些示例代码:
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")
}
}
}
我点击注释,然后:
>> tapped parent
>> tapped child
我点击地图,然后:
>> tapped parent
编辑:
我已经尝试过了,但没有成功:
另一个受 @Gergely Kovacs 的上述答案启发的小技巧!
var body: some View {
Map(coordinateRegion: $region, interactionModes: [.zoom, .pan], annotationItems: AnnotationItem.sample) { annotation in
MapAnnotation(coordinate: annotation.location.coordinate) {
VStack {
Circle()
.foregroundColor(.red)
.frame(width: 50)
Text(annotation.name)
}
.onLongPressGesture(minimumDuration: .zero, maximumDistance: .zero) {
print(">> Tapped child")
}
}
}
.onTapGesture {
print(">> tapped parent")
}
}
onLongPressGesture
的优先级高于 onTapGesture
目前,一个小技巧似乎有效!我将 onTap 手势更改为最小距离为 0 的 DragGesture。
var body: some View {
Map(coordinateRegion: $region, interactionModes: [.zoom, .pan], annotationItems: AnnotationItem.sample) { annotation in
MapAnnotation(coordinate: annotation.location.coordinate) {
VStack {
Circle()
.foregroundColor(.red)
.frame(width: 50)
Text(annotation.name)
}.gesture(childTapGesture)
}
}
.onTapGesture {
print(">> tapped parent")
}
}
let childTapGesture = DragGesture(minimumDistance: 0).onEnded {_ in
print(">> Tapped child")
}
}
而且它有效! 此解决方案的问题在于,在拖动地图时触摸其中一个图钉会无意中触发图钉操作。因此我的答案不会被接受
警告: 下面编辑中显示的解决方法显然仅在特殊情况下有效,请参阅下面 Gergely Kovacs 的评论。
在我看来,这是一个错误,因为默认行为是一次仅触发一个手势识别器,请参阅此处。
类似的问题也出现在
ScrollView
中,但是存在一个属性 .delaysContentTouches
可以解决它,请参见 here。不幸的是,对于 View
来说,这并不存在。ContentView
添加一个 @State var childTapTriggered = false
并将其设置为 var
(如果触发)。然后你可以使用类似的父点击手势关闭true
编辑(由于 Gergely Kovacs 的评论):上述解决方法不起作用,抱歉,请参阅评论。
但我测试了以下解决方法,它适用于我的情况:
我向 ContentView 添加了一个状态变量:
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
if !childTapTriggered {
// do parent action
}
}
在注释视图(子视图)上,我有以下修饰符:
@State var childTapped = false
在地图(父级)上我有以下修改器:
.onTapGesture {
print(">> tapped child")
childTapped = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
childTapped = false
}
}
当然,这又是一个黑客行为,并且必须正确调整将
.onTapGesture {
if !childTapped {
print(">> tapped parent")
}
}
重置为
childTapped
的延迟。无论如何,也许这可以解决您的问题!
false
并且注释现在将始终捕获点击,而不是地图有时捕获它。这不仅避免了黑客的解决方法,而且还清楚地在代码中表明了预期的行为。