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

时间:2019-06-12 13:41:16

标签: swift mapkit swiftui xcode11

我正在使用SwiftUI显示地图,如果用户点击了注释,它将在VStack中弹出一个详细视图。我已经制作了地图视图,并在另一个SwiftUI文件中插入了注释。我还做了详细视图。

如何在主视图文件中访问该地图的注释以定义一个.tapaction,以便他们将其用于详细视图?

我尝试将视图定义为MKMapView,但是无法在另一个SwiftUI视图中为UIViewRepresentable做到这一点。

主视图(ContentView)代码为:

struct ContentView: View {
    @State private var chosen = false

    var body: some View {

        VStack {
            MapView()
            .edgesIgnoringSafeArea(.top)
            .frame(height: chosen ? 600:nil)
            .tapAction {
            withAnimation{ self.chosen.toggle()}
            }

    if chosen {
        ExtractedView()
            }
        }
    }
}

MapView代码为:

struct MapView : UIViewRepresentable {
    @State private var userLocationIsEnabled = false
    var locationManager = CLLocationManager()
    func makeUIView(context: Context) -> MKMapView {
    MKMapView(frame: .zero)

    }
    func updateUIView(_ view: MKMapView, context: Context) {

        view.showsUserLocation = true

        .
        .
        .

            let sampleCoordinates = [
                CLLocation(latitude: xx.xxx, longitude: xx.xxx),
                CLLocation(latitude: xx.xxx, longitude: xx.xxx),
                CLLocation(latitude: xx.xxx, longitude: xx.xxx)
                ]
            addAnnotations(coords: sampleCoordinates, view: view)

        }
    }

}

我希望能够访问地图视图注释并在另一个视图中定义拍击动作。

1 个答案:

答案 0 :(得分:2)

在SwiftUI DSL中,您不访问视图。

相反,您可以将它们的“表示”组合起来以创建视图。

一个图钉可以由一个对象表示-操作该图钉也会更新地图。

这是我们的pin对象:

class MapPin: NSObject, MKAnnotation {

    let coordinate: CLLocationCoordinate2D
    let title: String?
    let subtitle: String?
    let action: (() -> Void)?

    init(coordinate: CLLocationCoordinate2D,
         title: String? = nil,
         subtitle: String? = nil,
         action: (() -> Void)? = nil) {
        self.coordinate = coordinate
        self.title = title
        self.subtitle = subtitle
        self.action = action
    }

}

这是我的Map,不仅是UIViewRepresentable,而且还使用了Coordinator

(有关UIViewRepresentable和协调员的更多信息,请参见WWDC 2019精彩演讲-Integrating SwiftUI

struct Map : UIViewRepresentable {

    class Coordinator: NSObject, MKMapViewDelegate {

        @Binding var selectedPin: MapPin?

        init(selectedPin: Binding<MapPin?>) {
            $selectedPin = selectedPin
        }

        func mapView(_ mapView: MKMapView,
                     didSelect view: MKAnnotationView) {
            guard let pin = view.annotation as? MapPin else {
                return
            }
            pin.action?()
            selectedPin = pin
        }

        func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
            guard (view.annotation as? MapPin) != nil else {
                return
            }
            selectedPin = nil
        }
    }

    @Binding var pins: [MapPin]
    @Binding var selectedPin: MapPin?

    func makeCoordinator() -> Coordinator {
        return Coordinator(selectedPin: $selectedPin)
    }

    func makeUIView(context: Context) -> MKMapView {
        let view = MKMapView(frame: .zero)
        view.delegate = context.coordinator
        return view
    }

    func updateUIView(_ uiView: MKMapView, context: Context) {

        uiView.removeAnnotations(uiView.annotations)
        uiView.addAnnotations(pins)
        if let selectedPin = selectedPin {
            uiView.selectAnnotation(selectedPin, animated: false)
        }

    }

}

想法是:

  • 图钉在包含地图的视图上是@State,并作为绑定向下传递。
  • 每次添加或移除图钉时,都会触发UI更新-所有图钉将被删除,然后再次添加(效率不高,但这超出了此答案的范围)
  • Coordinator是地图委托人-我可以从委托方法中检索触摸过的MapPin

进行测试

struct ContentView: View {

    @State var pins: [MapPin] = [
        MapPin(coordinate: CLLocationCoordinate2D(latitude: 51.509865,
                                                  longitude: -0.118092),
               title: "London",
               subtitle: "Big Smoke",
               action: { print("Hey mate!") } )
    ]
    @State var selectedPin: MapPin?

    var body: some View {
        NavigationView {
            VStack {
                Map(pins: $pins, selectedPin: $selectedPin)
                    .frame(width: 300, height: 300)
                if selectedPin != nil {
                    Text(verbatim: "Welcome to \(selectedPin?.title ?? "???")!")
                }
            }
        }

    }

}

...并尝试放大/敲击英国伦敦的图钉:)

enter image description here