google-mapsswiftuiuiviewrepresentable

Google Maps SDK heatmap wont update upon array append


EDIT: Look at my comment in the solution to see what I changed in the updateUIView.

I am trying to update a heatmap I have on my map via a button press.

Here is how the screen looks:

GIF of how the screen and interaction look

I have the array that the Map uses set to an @Binding variable and the array itself is initialized as an @State variable. The original preset values (as can be seen in the code below) appear on the map but the modified coordinate does not.

import SwiftUI
import GoogleMaps
import GoogleMapsUtils

struct GoogleMapsViewContainer: View {
    @ObservedObject var locationManager = LocationManager()

    @State var heatmapWeightedData: [GMUWeightedLatLng] = [
        GMUWeightedLatLng(coordinate: CLLocationCoordinate2D(latitude: -33.86, longitude: 151.20), intensity: 1),
        GMUWeightedLatLng(coordinate: CLLocationCoordinate2D(latitude: -33.8623, longitude: 151.2003), intensity: 3),
        GMUWeightedLatLng(coordinate: CLLocationCoordinate2D(latitude: -33.8635, longitude: 151.2010), intensity: 3),
        GMUWeightedLatLng(coordinate: CLLocationCoordinate2D(latitude: -33.8587, longitude: 151.1970), intensity: 3),
        GMUWeightedLatLng(coordinate: CLLocationCoordinate2D(latitude: -33.8579, longitude: 151.1987), intensity: 5)
    ]

    @Environment(.dismiss) var dismiss

    var body: some View {
        VStack {
            HStack {
                Spacer()
                Button("Done") {
                    dismiss()
                }
                .padding(.top, 45.0)
            }
            .padding(.trailing, 30.0)
            ZStack {
                GoogleMapsView(heatmapWeightedData: $heatmapWeightedData)
                VStack {
                    Spacer()

                    Button(action: addTrashLocation) {
                        Text("Mark Trash in My Area")
                    }
                    .padding()
                    .overlay(
                        RoundedRectangle(cornerRadius: 30)
                        .stroke(/@START_MENU_TOKEN@/Color(hue: 0.373, saturation: 0.717, brightness: 0.466)/@END_MENU_TOKEN@/, lineWidth: 2)
                    )
                    .background(.white)
                    .cornerRadius(30)
                    .padding(.bottom, 50.0)
                }
            }
        }
        .padding(.top, -30)
        .ignoresSafeArea()
    }

    func addTrashLocation() {
        self.heatmapWeightedData.append(GMUWeightedLatLng(coordinate: CLLocationCoordinate2D(latitude: -33.8606, longitude: 151.2011), intensity: 5))
    }
}

This below is the GoogleMapsView function the above is referencing.

import SwiftUI
import GoogleMaps
import GoogleMapsUtils

struct GoogleMapsView: UIViewRepresentable {

    @ObservedObject var locationManager = LocationManager()

    var marker: GMSMarker = GMSMarker()

    @Binding var heatmapWeightedData: [GMUWeightedLatLng]

    func makeUIView(context: Context) -> GMSMapView {
        let camera = GMSCameraPosition.camera(withLatitude: locationManager.latitude, longitude: locationManager.longitude, zoom: 15)
        let mapView = GMSMapView.map(withFrame: CGRect.zero, camera: camera)

        let heatmapLayer = GMUHeatmapTileLayer()
        heatmapLayer.radius = 150
        heatmapLayer.weightedData = heatmapWeightedData
        heatmapLayer.map = mapView

        return mapView
    }

    func updateUIView(_ mapView: GMSMapView, context: UIViewRepresentableContext<GoogleMapsView>) {
        marker.position = CLLocationCoordinate2D(latitude: locationManager.latitude, longitude: locationManager.longitude)
        marker.title = "My location"
        marker.map = mapView

        let heatmapLayer = GMUHeatmapTileLayer()
        heatmapLayer.radius = 150
        heatmapLayer.weightedData = heatmapWeightedData
        heatmapLayer.map = mapView

        mapView.animate(toLocation: CLLocationCoordinate2D(latitude: -33.86, longitude: 151.20))
    }

}

In order to be able to use the Google Maps SDK in SwiftUI, I went through a lot of trouble messing around with UIViewRepresentable and an AI since the internet couldn't help me with my answers. This situation has led me to many problems and I would not be surprised if this is somehow not working due to some nuance in the UIViewRepresentable I am unaware of.

How do I get the map to update the location?


Solution

  • You should use @StateObject to initialize ObservableObjects

    @StateObject var locationManager = LocationManager()
    

    Then use @ObserveObject or @EnvironmentObject to pass around.

    @ObservedObject var locationManager : LocationManager
    

    https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app

    Every time you call LocationManager() you are creating a different instance. One does not know about the other.