1

几天来,我一直在努力让 SwiftUI 与 MKMapView 一起工作。借助此处的帖子

在主(ContentView)SwiftUI 视图中以 UIViewRepresentable 的形式访问 MKMapView 元素

和网上这样的文章

https://www.hackingwithswift.com/books/ios-swiftui/advanced-mkmapview-with-swiftui

我已经设法让一些工作。点击选择放置的圆圈并在选择时以蓝色突出显示它们。然而,我遇到的问题是,每当我向我的圆圈数组中添加一个新圆圈时,所选圆圈就会丢失。

示例应用截图

我已经为此上传了一个测试应用程序压缩包代码道歉,我的 Swift 知识生疏了,我的 SwiftUI 和 MapKit 更是如此

该应用程序具有以下结构

  • 内容视图
    • 在 zstack 中包含我的 MapView 的主 SwiftUI 视图,带有一些按钮
    • 包括以下@States
      • @State private var circleRegions = MKCircle
      • @State private var selectedCircleIndex : Int?
      • @State private var selectedCircle:MKCircle?= 无
  • 地图视图
    • UIViewRepresentable 包装了 MKMapView,并有一个 Coordinator 来处理 MKMapView 回调
    • 包括以下 @Bindings 到主要的 ContentView 状态变量
      • @Binding var circleRegions : [MKCircle]
      • @Binding var selectedCircleIndex : Int?
      • @Binding var selectedCircle:MKCircle?
  • 协调员
    • 将 MapView 作为其父属性并实现所需的 MKMapViewDelegate 方法(包括我们的 mapTapped 方法)的协调器

当 MapView 调用其 MakeUIView() 时,它会创建 MKMapView 并将 Coordinator 设置为映射委托并添加点击手势识别器,如下所示

    mapView.addGestureRecognizer(UITapGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.mapTapped(_:))))

这个 mapTapped 处理立即向上到它的 MapView 父级来处理点击。

一旦点击被处理到世界空间中,我会根据 circleRegions 数组的@Binding 属性对其进行检查,以确定区域选择是否已更改。然后我更新 selectedCircleIndex 和 selectedCircle @Binding 属性以反映选择,然后我强制重建 MKMapView 的覆盖以确保我可以为这些覆盖创建新的渲染器,此时我可以更新渲染器的颜色以反映选择。

这一切都很好,尽管覆盖在删除和重新添加时会闪烁,但不是世界末日,除了删除和重新添加所有覆盖之外,我找不到更新渲染器颜色的更好方法。

问题是,如果我选择一个圆圈使其变为蓝色,然后稍微滚动地图,然后按圆圈按钮在另一个地方创建一个新圆圈,那么我剩下的两个圆圈都显示为红色,而最后一个选择的那个应该是蓝色的,仍然“选中”(选中的圆圈和索引是正确的)但错误地显示为红色。

对此进行调试,似乎 MapView 的 circleRegions 数组 @Binding 属性在协调器的 mapView(:rendererFor) MKMapViewDelegate 调用期间变为无效空。这会阻止我的代码将圆圈的索引与父级的 circleRegions 进行匹配。更令人困惑的是,如果您在代码中的这一点放置断点,您可以向上堆栈到父 MapView 执行 handleTap() 并调用rebuildOverlays() 时。此时它的 circleRegions 是正确的,但是当我们点击渲染器回调时,它们是无效的。

Swift 使您很难在调用堆栈中的这些不同位置看到 Coordinator 的父 MapView 是同一个实例,因为您无法在不同时间轻松查看 MapView 自身或 Coordinator 父级的指针值。

为了尝试调试或修复这个问题,我添加了一些额外的配置来改变行为

  • 地图视图
    • 让 dispatchMapViewUpdates = false
      • 它控制在点击手势识别器回调中发生的rebuildOverlays() 是立即完成还是分派回主队列
  • 协调员
    • let testWithIndex = true // 更新它以使用索引或可选圆圈进行测试
    • 哪个控制地图渲染器委托方法是使用选定的索引还是选定的 MKCircle?在父级上判断是否选择了正在渲染的圆圈

可悲的是,这些选项都没有解决这个问题,这让我对正在发生的事情感到困惑。我还确认此问题发生在与模拟器相同的设备上,因此它不是仅模拟器的 SwiftUI 问题。

如果有人有任何想法,我当然会很感激。干杯

4

0 回答 0