SwiftUI中Coordinator访问Binding数组始终为空的问题
SwiftUI与MKMapView交互时Coordinator访问Binding为空的问题
我在使用SwiftUI结合MKMapView开发时遇到了Coordinator相关的问题:我给UIViewRepresentable传递了一个Binding类型的routes数组,需要在Coordinator中访问这个Binding来设置折线的strokeColor。但实际运行时,Coordinator里访问parent.routes总是空数组;而在MapView的updateUIView方法里断点查看,这个Binding明明是有值的。
以下是相关代码:
import SwiftUI import MapKit struct MapView: UIViewRepresentable { var region: MKCoordinateRegion @Binding var routes: [RouteData] func makeUIView(context: Context) -> MKMapView { let mapView = MKMapView() mapView.delegate = context.coordinator mapView.showsUserLocation = true mapView.setRegion(region, animated: true) addOverlays(mapView) return mapView } func updateUIView(_ view: MKMapView, context: Context) { addOverlays(view) removeOverlays(view) } private func addOverlays(_ view: MKMapView) { for route in routes { for point in route.points { let waypoints = point.waypoints let polyline = MKPolyline(coordinates: waypoints, count: waypoints.count) polyline.title = route.routeID view.addOverlay(polyline) } } } private func removeOverlays(_ view: MKMapView) { for overlay in view.overlays { if let routeID = overlay.title!, routes.first(where: { $0.routeID == routeID }) == nil { view.removeOverlay(overlay) } } } func makeCoordinator() -> Coordinator { Coordinator(self) } } class Coordinator: NSObject, MKMapViewDelegate { let parent: MapView init(_ parent: MapView) { self.parent = parent } func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { if let routePolyline = overlay as? MKPolyline { let renderer = MKPolylineRenderer(polyline: routePolyline) // 这里总是打印空数组,但updateUIView里的routes明明有值 print("parents routes: \(self.parent.routes)") if let title = routePolyline.title, let route = self.parent.routes.first(where: { $0.routeID == title }) { renderer.strokeColor = UIColor(convertRGBStringToColor(color: route.route.rtclr)) } else { renderer.strokeColor = UIColor.blue } renderer.lineWidth = 5 return renderer } return MKOverlayRenderer() } }
问题原因
Coordinator里保存的parent是MapView的快照副本,并非当前最新的视图实例。SwiftUI触发updateUIView时会更新MapView的状态,但Coordinator持有的还是初始化时的旧实例,导致访问的routes始终是旧值(空数组)。
解决办法
方法一:让Coordinator直接持有Binding
修改Coordinator的初始化逻辑,直接绑定routes而不是整个MapView实例,这样能始终拿到最新值:
class Coordinator: NSObject, MKMapViewDelegate { @Binding var routes: [RouteData] init(routes: Binding<[RouteData]>) { self._routes = routes } func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { if let routePolyline = overlay as? MKPolyline { let renderer = MKPolylineRenderer(polyline: routePolyline) if let title = routePolyline.title, let route = self.routes.first(where: { $0.routeID == title }) { renderer.strokeColor = UIColor(convertRGBStringToColor(color: route.route.rtclr)) } else { renderer.strokeColor = UIColor.blue } renderer.lineWidth = 5 return renderer } return MKOverlayRenderer() } }
同步修改makeCoordinator方法:
func makeCoordinator() -> Coordinator { Coordinator(routes: $routes) }
方法二:在updateUIView中更新Coordinator的父视图引用
如果需要保留对MapView的完整引用,可将Coordinator的parent改为可变属性,并在updateUIView中更新为最新实例:
class Coordinator: NSObject, MKMapViewDelegate { var parent: MapView // 将let改为var init(_ parent: MapView) { self.parent = parent } // 其余代码保持不变 }
在updateUIView中添加更新逻辑:
func updateUIView(_ view: MKMapView, context: Context) { context.coordinator.parent = self // 同步最新的MapView实例 addOverlays(view) removeOverlays(view) }
额外优化:避免重复添加Overlay
当前addOverlays每次触发都会重复添加所有折线,可能引发性能问题,可添加存在性检查:
private func addOverlays(_ view: MKMapView) { for route in routes { for point in route.points { let waypoints = point.waypoints let polyline = MKPolyline(coordinates: waypoints, count: waypoints.count) polyline.title = route.routeID // 仅添加不存在的折线 if !view.overlays.contains(where: { $0.title == polyline.title }) { view.addOverlay(polyline) } } } }
内容的提问来源于stack exchange,提问作者Obito
相关产品推荐
相关产品推荐

