Home > Enterprise >  How do I declare a view so it's initialization and itself are NOT in the representable view?
How do I declare a view so it's initialization and itself are NOT in the representable view?

Time:02-07

I have a UIKit representable view for using Mapbox in my SwiftUI app and control the mapView from inside the Coordinator by passing the MapboxViewRepresentable View itself when making the coordinator.

The problem I am having is that the mapView, its coordinator and subscriptions are all being created TWICE! While trying to figure out what the problem was someone commented that my problem was "holding on to the view in a variable in the representable. Then the initialization and the view are in the make. Don’t put it on a variable at struct level"

That makes sense but how would I restructure my code?? Would the mapView become a global variable that can be manipulated everywhere??

I am not sure how to properly restructure this so that the mapView is NOT stored in the representable view AND how I should re-configure how I control the map; For example adding annotations, animating its position, etc...

Right now I control the map from the variable declared in the representable: control.mapView.doSomething AND access its view model the same way: control.mapVM.property, control.mapVM.func

Thank you

Container View

struct MapboxMapView: View {
    @ObservedObject var mapVM : MapViewModel
    @ObservedObject var popupVM : PopupProfileViewModel
    
    var body: some View {
        ZStack(alignment: .bottom) {
            MapboxMapViewRepresentable()
                .environmentObject(self.mapVM)
                .environmentObject(self.popupVM)
        }
    }
}

REPRESENTABLE VIEW AND COORDINATOR

struct MapboxMapViewRepresentable: UIViewRepresentable {
    @EnvironmentObject var session: SessionStore
    @EnvironmentObject var mapVM : MapViewModel
    @EnvironmentObject var popupVM: PopupProfileViewModel
   
    let mapView: MGLMapView = MGLMapView(frame: .zero, styleURL: URL(string: MAPBOX_STYLE_URL))
    // Where should I decleare this view??? Can it be a global???

    func makeCoordinator() -> Coordinator {
        let coordinator = Coordinator(control: self)
        return coordinator
    }
    
    func makeUIView(context: Context) -> MGLMapView {
        return mapView
    }
    
    func updateUIView(_ mapView: MGLMapView, context: Context) {/code/}


    class Coordinator: NSObject  {

        let locationManager = CLLocationManager()
        var control: MapboxMapViewRepresentable
        var subscriptions = Set<AnyCancellable>()

        init(control: MapboxMapViewRepresentable) {
            self.control = control
            super.init()
            setupLocationManager()

            // subscriber receives data via publisher to manipulate map
            control.mapVM.addAnnotations.sink { annotations in
                control.mapView.addAnnotations(annotations)
              // ^ aways runs twice because current structure 
              // make two views and coordinators
            }.store(in: &subscriptions)
        }

        deinit {
            subscriptions.forEach{$0.cancel()}
            subscriptions.removeAll()
        }
    }

}

CodePudding user response:

You can pass a reference to the UIView to your Coordinator. That might look like this:

struct MapboxMapViewRepresentable: UIViewRepresentable {
    @EnvironmentObject var session: SessionStore
    @EnvironmentObject var mapVM : MapViewModel
    @EnvironmentObject var popupVM: PopupProfileViewModel
   
    func makeCoordinator() -> Coordinator {
        let coordinator = Coordinator(control: self)
        return coordinator
    }
    
    func makeUIView(context: Context) -> MGLMapView {
        let view = MGLMapView(frame: .zero, styleURL: URL(string: MAPBOX_STYLE_URL))
        context.coordinator.mapView = view
        return view
    }
    
    func updateUIView(_ mapView: MGLMapView, context: Context) {
        //code
    }

    class Coordinator: NSObject  {

        let locationManager = CLLocationManager()
        var control: MapboxMapViewRepresentable
        var subscriptions = Set<AnyCancellable>()
        
        var mapView : MGLMapView? = nil {
            didSet {
                // subscriber receives data via publisher to manipulate map
                mapView?.addAnnotations.sink { annotations in
                    //annotation sink
                }.store(in: &subscriptions)
            }
        }

        init(control: MapboxMapViewRepresentable) {
            self.control = control
            super.init()
            setupLocationManager()
        }
    }
}
  •  Tags:  
  • Related