如何在 Map 和 MapAnnotation 上同时使用 onTap 手势,而不使两者互相干扰?

问题描述 投票:0回答:4

我有一个带有 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

编辑:

我已经尝试过了,但没有成功:

  • 使父级操作依赖于布尔值,该布尔值设置为防止点击子级时地图的操作。见评论:我只能推迟父母的行动,不能取消。
  • 为每个添加自定义点击手势,并在其中一个上设置 .exclusivelyBefore(:) 修饰符
ios swift swiftui mapkit swiftui-ontapgesture
4个回答
4
投票

另一个受 @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 
  • 不能以相反的顺序工作,例如父级的 onLongPressGesture 和子级的 onTapGesture!

1
投票

目前,一个小技巧似乎有效!我将 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")
    }
} 

而且它有效! 此解决方案的问题在于,在拖动地图时触摸其中一个图钉会无意中触发图钉操作。因此我的答案不会被接受


0
投票

警告: 下面编辑中显示的解决方法显然仅在特殊情况下有效,请参阅下面 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
的延迟。
无论如何,也许这可以解决您的问题!


0
投票

false

并且注释现在将始终捕获点击,而不是地图有时捕获它。这不仅避免了黑客的解决方法,而且还清楚地在代码中表明了预期的行为。

© www.soinside.com 2019 - 2024. All rights reserved.