【问题标题】:How do I declare a view so it's initialization and itself are NOT in the representable view?如何声明一个视图,使其初始化并且它本身不在可表示的视图中?
【发布时间】:2022-02-08 02:47:40
【问题描述】:

我有一个 UIKit 可表示视图,用于在我的 SwiftUI 应用程序中使用 Mapbox,并通过在制作协调器时传递 MapboxViewRepresentable 视图本身来控制协调器内部的 mapView。

我遇到的问题是 mapView、它的协调器和订阅都被创建了两次!在试图弄清楚问题是什么时,有人评论说我的问题是“在可表示的变量中保留视图。然后初始化和视图在制作中。不要把它放在变量上在结构级别”

所以我调整了我的代码并在 makeUIView 中传递了 mapView,而不是在 MapboxMapViewRepresentable 中声明它,并在视图通过后使用 Coordinator 内部的 didSet 创建/保存订阅。

由于我需要通过 MapboxMapViewRepresentable.Coordinator 方法操作 environmentObjects,我在 Coordinator 中声明了一个控件属性(super.init() 也需要)。

我不知道为什么一切仍然被创建两次,我不认为我使用扩展的事实是这样做的??

还有什么我能想到的检查吗???

谢谢

父级结构和代表性

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

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
        view.showsUserLocation = true
        view.setCenter(CLLocationCoordinate2D(latitude: mapVM.usersCurrentLat, longitude: mapVM.usersCurrentLong), zoomLevel: MapViewModel.startAtZoomLevel, animated: false)
        view.delegate = context.coordinator
        view.maximumZoomLevel = MapViewModel.maxZoomLevel
        view.allowsRotating = false
        view.direction =  0.0
        view.allowsTilting = false
        mapVM.zoomLevel = view.zoomLevel
        return view
    }
    
    func updateUIView(_ mapView: MGLMapView, context: Context) { }
    
}

协调器(MapboxMapViewRepresentable 的扩展)


extension MapboxMapViewRepresentable {

    class Coordinator: NSObject  {
        var control: MapboxMapViewRepresentable
        let locationManager = CLLocationManager()
        var subscriptions = Set<AnyCancellable>()
        
        // MARK: NEW
        var mapView : MGLMapView? {
           didSet {
               guard let mapView = mapView else { return }

                // all of these run twice since map and coordinator are created twice
               control.mapVM.placePublisher.sink { bool in
                   guard let self = self else { return }
                   // control.mapVM.doSomething
                   print(bool)
               }.store(in: &subscriptions)
               
               Self.showRandomPlace.sink { location in
                  // mapView.setCenter....
                  // control.popupVM.doSomething()
                   print(location)
               }.store(in: &subscriptions)

               control.mapVM.newUserSelectedPublisher.sink { nearestPlaceCoords in
                   DispatchQueue.main.async {
                       mapView.zoomLevel = 15.9
                       mapView.setCenter(CLLocationCoordinate2D(latitude: nearestPlaceCoords.lat, longitude: nearestPlaceCoords.long), animated: false)
                   }
               }.store(in: &subscriptions)
           }
        }
        
        // publishes zoomLevel with coordiantor method regionIsChanging to update viewModel with current zoomLevel
        let zoomLevelPublisher = PassthroughSubject<Double, Never>()
        
        var previosZoomLevel = MapViewModel.maxZoomLevel
             
        init(control: MapboxMapViewRepresentable) {
            self.control = control
            super.init()
            setupLocationManager()
            
            // set inital map view's center to user's/device's current location
            if let coordinate = locationManager.location?.coordinate  {
                guard let mapView = mapView else { return }

                self.setCenterAt(coordinate: coordinate)
                control.mapVM.mapCenterCoordinate = mapView.centerCoordinate
            }
        } // END INIT
        
        deinit {
            subscriptions.forEach{$0.cancel()}
            subscriptions.removeAll()
        }
    }
}

委托方法是如何声明的

extension MapboxMapViewRepresentable.Coordinator  {
   
    func mapView(_: MGLMapView, didFinishLoading style: MGLStyle) {
        print("DID FINISH LOADING STYLE")
        
        self.control.mapVM.didFinishLoadingUrlStyle = true
        
        self.control.mapVM.loadCurrentUsersPlaces()
        self.control.mapVM.loadNearestPlaces()
        
    }
}

【问题讨论】:

    标签: ios swift uikit mapbox-ios uiviewrepresentable


    【解决方案1】:

    您可以将对UIView 的引用传递给您的Coordinator。可能看起来像这样:

    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()
            }
        }
    }
    

    【讨论】:

    • 这是否意味着我可以摆脱协调器中的控制属性?谢谢!
    • 很可能,是的——除非您必须通过该属性操作其他东西(例如环境对象之一)。
    • 你摇滚!非常感谢
    • 我在 makeUIView 中传递了引用,就像在您的代码中一样,并且正在使用控制,因为它是协调器中的 super.init() 所必需的,并且由于视图具有我需要操作的 environmentObjects 但视图/协调器仍在创建两次???我不知道为什么......
    • @Chris 你需要发布一个minimal reproducible example 来调试它。这很可能是您目前所代表的代码中未反映的某些东西的副作用。
    猜你喜欢
    • 1970-01-01
    • 2015-04-27
    • 1970-01-01
    • 1970-01-01
    • 2016-11-23
    • 2015-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多