如何在用户触摸 SwiftUI 中的 GoogleMaps 标记时更改 UI 状态?

How to change UI state when user touch GoogleMaps' Marker in SwiftUI?

这个问题是关于 SwiftUI 的。

我正在尝试显示地图并允许用户触摸任何可用的标记。当它发生时,我希望更改我视图中的文本,以反映该用户的操作。

经过大量搜索,我认为解决方案可以接近 Observable 协议,但我就是想不出正确的方法。这是我的代码:


struct Home: View {

    // Here's the attribute I want to be changed when user touches the marker
    var selectedMarker: GMSMarker?

    var body: some View {

            VStack(spacing: 0) {

                // Condition to be applied when user touches the marker                                
                if (selectedMarker == nil){
                    Text("No marker selected").padding()
                }else{
                    Text("Now, there's a marker selected").padding()
                }

                GoogleMapsHome()

        }
        .navigationBarBackButtonHidden(true)
        .navigationBarTitle(Text("Marker question"), displayMode: .inline)

    }

}

struct Home_Previews: PreviewProvider {
    static var previews: some View {
        Home()
    }
}

这是 GoogleMaps 定义:

struct GoogleMapsHome: UIViewRepresentable {

    private let zoom: Float = 18

    // Just for didactic purposes. Later, I'm going to use LocationManager
    let lat: Double = -15.6692660716233
    let lng: Double = -47.83980712156295

    func makeUIView(context: Self.Context) -> GMSMapView {

        let camera = GMSCameraPosition.camera(
            withLatitude: lat,
            longitude: lng,
            zoom: zoom)

        let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)

        mapView.mapType = .hybrid

        mapView.delegate = context.coordinator

        return mapView

    }

    func updateUIView(_ mapView: GMSMapView, context: Context) {

        mapView.animate(toLocation: CLLocationCoordinate2D(latitude: lat, longitude: lng))

        let position = CLLocationCoordinate2D(latitude: lat, longitude: lng)
        let marker = GMSMarker(position: position)

        marker.title = "You"

        marker.map = mapView

    }

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

    class Coordinator: NSObject, GMSMapViewDelegate, ObservableObject {

        let owner: GoogleMapsHome       // access to owner view members,

        init(owner: GoogleMapsHome) {
         self.owner = owner
        }

        @Published var selectedMarker: GMSMarker? {
            willSet { objectWillChange.send() }
        }

        func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {

            print("A marker has been touched by the user")

            self.selectedMarker = marker

            return true

        }

    }
}

我希望有人能帮助我,以后这个问题对任何有同样需求的人都有用。

此致!

过了一段时间,我找到了解决的方法。

关键字是 "Coordinator" 和 "Binding"。

当然,我不确定这是正确的方法还是最好的方法,但至少它有效。


import Foundation
import SwiftUI
import GoogleMaps

struct Home: View {

    @State var selectedMarker: GMSMarker?

    var body: some View {

            VStack(spacing: 0) {

                if (selectedMarker == nil){
                    Text("No marker selected").padding()
                }else{
                    Text("There's a marker selected").padding()
                }

                GoogleMapsHome(selectedMarker: self.$selectedMarker)

        }
        .navigationBarBackButtonHidden(true)
        .navigationBarTitle(Text("Map Test"), displayMode: .inline)

    }

}

struct Home_Previews: PreviewProvider {
    static var previews: some View {
        Home()
    }
}

struct GoogleMapsHome: UIViewRepresentable {

    private let zoom: Float = 18

    let lat: Double = -15.6692660716233
    let lng: Double = -47.83980712156295

    @Binding var selectedMarker: GMSMarker?

    func makeCoordinator() -> Coordinator {
        return Coordinator(
            owner: self,
            selectedMarker: $selectedMarker)
    }

    func makeUIView(context: Self.Context) -> GMSMapView {

        let camera = GMSCameraPosition.camera(
            withLatitude: lat,
            longitude: lng,
            zoom: zoom)

        let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)

        mapView.mapType = .hybrid

        mapView.delegate = context.coordinator

        return mapView

    }

    func updateUIView(_ mapView: GMSMapView, context: Context) {

        mapView.animate(toLocation: CLLocationCoordinate2D(latitude: lat, longitude: lng))

        let position = CLLocationCoordinate2D(latitude: lat, longitude: lng)
        let marker = GMSMarker(position: position)

        marker.title = "You"

        marker.map = mapView

    }


    class Coordinator: NSObject, GMSMapViewDelegate, ObservableObject {

        let owner: GoogleMapsHome       // access to owner view members,

        @Binding var selectedMarker: GMSMarker?

        init(
            owner: GoogleMapsHome,
            selectedMarker: Binding<GMSMarker?>
        ) {

            self.owner = owner

            _selectedMarker = selectedMarker

        }

        func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {

            print("A marker has been touched")

            self.selectedMarker = marker

            return true

        }

    }
}