1

After reading other questions and google around, i think its safe to say that updateUIView will be triggered as soon as any @state/@Binding var is changed in UIViewRepresentable no matter what. In my case i don't want to trigger updateUIView on one specific @State var.

I'm using Google maps in my app where I'm using @state/@Binding var to pass some location list data to UIViewRepresentable, the updateUIView do the list iteration and shows the location markers on mapView with GMSCameraUpdate.fit(bounds,withPadding: 15.0).

Now i'm using coordinator to use GMSMapViewDelegate methods like drag and willMove and idleAt. I want to pass a bool which is a @State var back to my content view as soon as the idleAt is triggered after camera move. The bool is sent back to the contentView and i can trigger my API to get new list of location data and pass it in @State var. But since the bool was a @State var and it got updated, the updateUIView is triggered still with the old list of data and move the camera back to old location bounds. This updateUIView is triggered even before the API call is fired.

How can i send the bool back to my contentView without triggering updateUIView?

UIViewRepresentable code

struct MapsView: UIViewRepresentable {

@Binding var list : [LocationObj]
@Binding var shouldRefresh: Bool

func makeUIView(context: Context) -> GMSMapView 

func updateUIView(_ mapView: GMSMapView, context: Context){
    var bounds = GMSCoordinateBounds()
    for location in list{}
    
    mapView.animate(with: GMSCameraUpdate.fit(bounds,withPadding: 15.0))

}

func makeCoordinator() -> Coordinator {
    Coordinator(owner: self, refresh: $shouldRefresh)
}

class Coordinator: NSObject, GMSMapViewDelegate {
    
    let owner: MapsView
    var mShouldRefresh: Binding<Bool>

    init(owner: MapsView, refresh: Binding<Bool>) {
        self.owner = owner
        self.mShouldRefresh = refresh
    }

func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition){ 
   // after moving camera and calculating the difference
    mShouldRefresh.wrappedValue = true
}

Now back in contentView

@State var shouldRefresh = true

MapsView(list: $locationList, shouldRefresh: $shouldRefresh)
Joe
  • 173
  • 1
  • 15
  • 1
    Don't use binding then, instead you can inject closure and call it, update will not be called because representable will not depend on that state anymore. – Asperi Dec 29 '21 at 06:48
  • @Asperi I don't understand how closure will work here. can you write some code snippet? – Joe Dec 29 '21 at 09:53

1 Answers1

3

Here is a sketch of possible approach (written inline, not tested, so might by typo - compiler error fixing is on you):

struct MapsView: UIViewRepresentable {

    @Binding var list : [LocationObj]
    var callback: (Bool) -> Void           // << here !!

    func makeUIView(context: Context) -> GMSMapView

    func updateUIView(_ mapView: GMSMapView, context: Context){
        var bounds = GMSCoordinateBounds()
        for location in list{}

        mapView.animate(with: GMSCameraUpdate.fit(bounds,withPadding: 15.0))

    }

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

    class Coordinator: NSObject, GMSMapViewDelegate {

        let owner: MapsView

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

        func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition){
            // after moving camera and calculating the difference
            owner.callback(true)      // << here !!
        }
    }
}

and usage like

@State var shouldRefresh = true

MapsView(list: $locationList) {
    self.shouldRefresh = $0
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Appreciate your time, i'll try and update. – Joe Dec 29 '21 at 10:09
  • I tried this closure as `MapsView(workersList: $locationList, callback: {self.shouldRefresh = $0})` but still the `updateUIView` is being fired. any idea? – Joe Dec 29 '21 at 10:51