【发布时间】: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