几天来,我一直在努力让 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() 是立即完成还是分派回主队列
- 让 dispatchMapViewUpdates = false
- 协调员
- let testWithIndex = true // 更新它以使用索引或可选圆圈进行测试
- 哪个控制地图渲染器委托方法是使用选定的索引还是选定的 MKCircle?在父级上判断是否选择了正在渲染的圆圈
可悲的是,这些选项都没有解决这个问题,这让我对正在发生的事情感到困惑。我还确认此问题发生在与模拟器相同的设备上,因此它不是仅模拟器的 SwiftUI 问题。
如果有人有任何想法,我当然会很感激。干杯