在主(ContentView)SwiftUI视图中以UIViewRepresentable的形式访问MKMapView元素 [英] Accessing MKMapView elements as UIViewRepresentable in the main (ContentView) SwiftUI view

查看:206
本文介绍了在主(ContentView)SwiftUI视图中以UIViewRepresentable的形式访问MKMapView元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am using SwiftUI to display a map and if user tapped on an annotation, it pops up a detail view in the VStack. I have made the map view and inserted annotations in another SwiftUI file. I also made the detail view.

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

How can I access the annotations of that map in the main view file to define a .tapaction for them to use it for the detailed view?

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

I tried defining the view as MKMapView but it is not possible to do it for a UIViewRepresentable inside another SwiftUI view.

主视图(ContentView)代码为:

The main view (ContentView) code is:

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代码为:

The MapView code is:

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)

        }
    }

}

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

I expect to be able to access map view annotations and define tapaction in another view.

推荐答案

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

In SwiftUI DSL you don't access views.

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

Instead, you combine "representations" of them to create views.

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

A pin can be represented by an object - manipulating the pin will also update the map.

这是我们的图钉对象:

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.

Here's my Map, which is not just UIViewRepresentable, but also makes use of a Coordinator.

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

(More about UIViewRepresentable and coordinators can be found in the excellent WWDC 2019 talk - 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.
  • The pins are a @State on the view containing the map, and are passed down as a binding.
  • Each time a pin is added or removed, it will trigger a UI update - all the pins will be removed, then added again (not very efficient, but that's beyond the scope of this answer)
  • The Coordinator is the map delegate - I can retrieve the touched MapPin from the delegate methods.

进行测试:

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 ?? "???")!")
                }
            }
        }

    }

}

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

...and try zooming/tapping the pin on London, UK :)

这篇关于在主(ContentView)SwiftUI视图中以UIViewRepresentable的形式访问MKMapView元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆