SwiftUI:与 MapKit 协调器通信

时间:2022-07-23
本文章向大家介绍SwiftUI:与 MapKit 协调器通信,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

将空的MKMapView嵌入 SwiftUI 很简单,但是如果您想对地图做任何有用的事情,则需要引入一个协调器——一个类,可以充当地图视图的委托,将数据往返于SwiftUI。

就像使用UIImagePickerController一样,这意味着创建一个从NSObject继承的嵌套类,使其符合我们的视图或视图控制器所使用的任何委托协议,并为其提供对父结构的引用,以便可以将数据传递回 SwiftUI。

对于地图视图,我们关心的协议是MKMapViewDelegate,因此我们可以立即开始编写协调器类。将其添加为MapView中的嵌套类:

class Coordinator: NSObject, MKMapViewDelegate {
    var parent: MapView

    init(_ parent: MapView) {
        self.parent = parent
    }
}

有了该类后,我们的代码将停止编译,因为SwiftUI可以看到我们有一个协调器类,并且想知道应该如何创建它。

就像UIViewControllerRepresentable协议一样,这意味着添加一个名为makeCoordinator()的方法,该方法会发回配置好的Coordinator实例。应该将其添加到MapView结构中,并将其发送给协调器,以便可以报告正在发生的事情。

因此,现在将此方法添加到MapView中:

func makeCoordinator() -> Coordinator {
    Coordinator(self)
}

现在,通过将以下代码行添加到makeUIView()方法,可以将其连接到我们的MKMapView

mapView.delegate = context.coordinator

这就完成了我们的配置,这意味着我们现在可以开始添加使协调器响应地图视图中活动的方法。记住,我们的协调器是地图视图的委托人,这意味着当发生有趣的事情时,它会得到通知——当地图移动时,开始并完成加载时,当用户位于地图上,触摸地图图钉时,等等。

MapKit 会自动检查我们的协调器类,以了解我们要告知的通知之一。它使用函数签名来做到这一点:如果找到具有精确名称和参数列表的方法,它将对其进行调用。

为了说明这一点,我们将添加一个名为mapViewDidChangeVisibleRegion()的方法,该方法需要一个MKMapView参数。是的,这个方法名称很长,但是请相信我,UIKit 中还有很多更长的——我个人最喜欢的方式早在iOS 5.0中就已弃用,被称为willAnimateSecondHalfOfRotationFromInterfaceOrientation()

无论如何,现在将此方法添加到Coordinator类中:

func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
    print(mapView.centerCoordinate)
}

每当地图视图更改其可见区域时,即当其移动,缩放或旋转时,都会调用该方法。我们所做的只是打印新的中心坐标,因此当您在模拟器中重新运行该应用程序时,您应该会在Xcode输出窗口中看到很多坐标正在打印。

地图视图协调器还负责在地图视图需要时提供更多信息。例如,我们可以在地图上添加标注,作为我们希望用户与之交互的兴趣点。这是模型数据,这意味着它只是标题和一些坐标,而不是数据的视觉表示,因此,当地图视图要渲染我们的标注时,它将询问协调器应显示什么。

为了说明这一点,我们将修改makeUIView()方法,以便发送伦敦市的标注,如下所示:

func makeUIView(context: Context) -> MKMapView {
    let mapView = MKMapView()
    mapView.delegate = context.coordinator

    let annotation = MKPointAnnotation()
    annotation.title = "London"
    annotation.subtitle = "Capital of England"
    annotation.coordinate = CLLocationCoordinate2D(latitude: 51.5, longitude: 0.13)
    mapView.addAnnotation(annotation)

    return mapView
}

MKPointAnnotation是符合MKAnnotation协议(MapKit用于显示标注)的类。您可以根据需要创建自己的标注类型,但是MKPointAnnotation在这里足够好,因为它可以让我们提供标题,字幕和坐标。如果您感到好奇,则名称CLLocationCoordinate2D以“ CL”开头,因为它来自另一个名为Core Location的Apple框架。

无论如何,这会在我们的地图上添加标注,并且您无需做进一步的工作就可以再次运行该应用程序,然后四处滚动直到找到伦敦——您应该会在此处看到可以点击以显示我们的字幕的标记。

如果您想自定义标记的外观,我们需要重新使用协调器。地图视图将在我们的协调器中查找名为mapView(_:viewFor :)的特定方法,如果存在,则会被调用。这可以创建自定义标注视图,但是Apple再次以MKPinAnnotationView的形式为我们提供了一种简洁的选择。

将此代码添加到Coordinator类:

func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
    let view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: nil)
    view.canShowCallout = true
    return view
}

如您所见,该方法将获得地图视图和标注,并且必须返回正确的视图以用于显示该标注。在我们的代码中,我们使用它来创建MKPinAnnotationView的实例,将其应使用的标注传递给它,然后将canShowCallout设置为 true,以便点击图钉可显示信息,然后将其返回。

London

在我们现在完成地图之前,我想简单地提到一下reuseIdentifier属性。创建视图非常昂贵,这就是为什么SwiftUI具有诸如Identifiable协议之类的东西的原因——如果它可以唯一地标识其视图,那么它就可以分辨出哪些视图已经更改,哪些视图没有更改,这意味着它可以最大程度地减少需要执行的工作量。

UIKit 和 MapKit 之类的框架具有相同概念的简单版本,称为重用标识符。这些字符串可以是我们想要的任何字符串,并允许框架保留大量视图以供重用。我们可以请求一个具有特定ID的对象(“给我一个带有地标标识符的大头针”),然后从数组中取回一个即可使用,这意味着我们无需再次创建它。

我们在上面指定了nil作为重用标识符,这意味着我们不想重用视图。当您只是学习时——以及实际上在任何时候仅使用少量图钉的情况下,都很好,但是稍后我将在这里向您展示更有效的路线,即重用视图。