You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 13:20:28