Доступ к элементам MKMapView как UIViewRepresentable в основном (ContentView) представлении SwiftUI - PullRequest
2 голосов
/ 12 июня 2019

Я использую SwiftUI для отображения карты, и если пользователь нажал на аннотацию, он открывает подробный вид в VStack.Я сделал вид карты и вставил аннотации в другой файл SwiftUI.Я также сделал подробный вид.

Как я могу получить доступ к аннотациям этой карты в файле основного вида, чтобы определить .tapaction для них, чтобы использовать его для подробного вида?

Я попытался определить вид как MKMapView, но онневозможно сделать это для UIViewRepresentable внутри другого представления SwiftUI.

Код основного представления (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 Ответ

2 голосов
/ 12 июня 2019

В SwiftUI DSL вы не получаете доступ к представлениям.

Вместо этого вы комбинируете их "представления" для создания представлений.

Пин может быть представлен объектом - манипулируя пиномтакже обновит карту.

Это наш пин-объект:

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 - Интеграция 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 на виде, содержащем карту, и передаются в виде привязки.
  • Каждый раз, когда пин-код добавляется или удаляется, он вызывает обновление пользовательского интерфейса -все контакты будут удалены, а затем добавлены снова (не очень эффективно, но это выходит за рамки этого ответа)
  • 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 ?? "???")!")
                }
            }
        }

    }

}

... и попробуйте zoomiнг / постукивание по булавке в Лондоне, Великобритания:)

enter image description here

...