如何正确更新UIViewRepresentable/UIViewControllerRepresentable的内容?
SwiftUI 地图异步加载GeoJSON叠加层及优化方案
1. 数据层实现(ViewModel)
负责异步加载、解析GeoJSON,仅在数据处理完成后更新可渲染叠加层数组,从根源减少不必要的视图刷新。
class MapViewModel: ObservableObject { // 仅在GeoJSON处理完成后才更新该数组,驱动视图刷新 @Published var overlays: [MKOverlay] = [] // 标记已加载的文件,避免重复解析 private var loadedGeoJSONNames: Set<String> = [] func loadGeoJSON(fileNames: [String]) async { for fileName in fileNames { guard !loadedGeoJSONNames.contains(fileName), let url = Bundle.main.url(forResource: fileName, withExtension: "geojson") else { continue } do { let data = try Data(contentsOf: url) let geoJSONContent = try MKGeoJSONDecoder().decode(data) // 提取所有可渲染的叠加层对象 let validOverlays = geoJSONContent.compactMap { $0 as? MKOverlay } // 主线程更新发布属性 DispatchQueue.main.async { self.overlays.append(contentsOf: validOverlays) self.loadedGeoJSONNames.insert(fileName) } } catch { print("GeoJSON解析失败: \(error.localizedDescription)") } } } }
2. 地图封装层实现(UIViewRepresentable)
优化updateUIViewController调用逻辑,仅在叠加层有实际变化时才执行添加操作,避免高频无意义刷新。
struct MapView: UIViewRepresentable { let overlays: [MKOverlay] let showUserLocation: Bool func makeUIView(context: Context) -> MKMapView { let mapView = MKMapView() mapView.showsUserLocation = showUserLocation mapView.delegate = context.coordinator return mapView } func updateUIView(_ uiView: MKMapView, context: Context) { // 比对已有叠加层和新叠加层的差集,无新增数据直接跳过操作 let existingOverlays = Set(uiView.overlays) let newOverlays = Set(overlays).subtracting(existingOverlays) guard !newOverlays.isEmpty else { return } // 仅添加新增的叠加层 uiView.addOverlays(Array(newOverlays)) } func makeCoordinator() -> Coordinator { Coordinator() } class Coordinator: NSObject, MKMapViewDelegate { // 叠加层渲染逻辑 func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer { if let polyline = overlay as? MKPolyline { let renderer = MKPolylineRenderer(overlay: polyline) renderer.strokeColor = .systemBlue renderer.lineWidth = 3 return renderer } else if let polygon = overlay as? MKPolygon { let renderer = MKPolygonRenderer(overlay: polygon) renderer.fillColor = .systemBlue.withAlphaComponent(0.3) renderer.strokeColor = .systemBlue renderer.lineWidth = 1 return renderer } return MKOverlayRenderer(overlay: overlay) } } }
3. 视图层调用
在SwiftUI视图中调用封装好的地图组件,触发异步加载逻辑。
struct ContentView: View { @StateObject private var viewModel = MapViewModel() var body: some View { MapView(overlays: viewModel.overlays, showUserLocation: true) .ignoresSafeArea() .task { // 页面加载完成后异步加载所有GeoJSON文件 await viewModel.loadGeoJSON(fileNames: ["route", "area", "poi"]) } } }
关键优化说明
- 控制update触发逻辑:
updateUIViewController中先做叠加层差集比对,没有新增数据时直接返回,跳过无意义渲染 - 状态更新收敛:所有GeoJSON加载、解析逻辑收拢在ViewModel层,仅将最终可用的叠加层通过
@Published发布,中间状态不会触发视图刷新 - 重复加载拦截:用Set存储已加载文件标识,避免同一文件被重复解析、重复添加叠加层
内容的提问来源于stack exchange,提问作者Michael Shikin
相关产品推荐
相关产品推荐

