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

如何正确更新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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 21:27:00