SwiftUI中updateUIView添加MKMapView标注卡顿,求替代调用方案
解决MKMapView在updateUIView中添加标注卡顿的问题
你遇到的卡顿问题是因为updateUIView会频繁触发(比如地图拖动、视图刷新时都会调用),每次在这里添加/移除大量标注会占用主线程资源,导致动画卡顿。以下是几种替代方案:
方案1:直接在按钮点击事件中处理
如果你的公交图标是SwiftUI的Button,可以直接在按钮的点击回调里获取地图实例并添加标注,避免在updateUIView中重复执行:
// 假设你有一个UIViewRepresentable的MapView封装 struct MapView: UIViewRepresentable { var nearbyBusStops: [BusStop] // 持有地图实例引用 private weak var mapView: MKMapView? func makeUIView(context: Context) -> MKMapView { let map = MKMapView() self.mapView = map return map } func updateUIView(_ uiView: MKMapView, context: Context) { // 这里仅处理地图基础配置,不涉及标注操作 } // 对外提供添加公交站点标注的方法 func addBusStopAnnotations() { guard let mapView = mapView else { return } // 只移除已有的公交站点标注,保留其他类型标注(如用户位置) let existingBusAnnotations = mapView.annotations.filter { $0 is BusStopAnnotation } mapView.removeAnnotations(existingBusAnnotations) // 创建并添加新标注 let annotations = nearbyBusStops.map { BusStopAnnotation(busStop: $0) } mapView.addAnnotations(annotations) } } // 在主视图中使用时,通过按钮触发标注添加 struct ContentView: View { @StateObject private var viewModel = NearbyBusStopsViewModel() @State private var mapViewRef: MapView? var body: some View { VStack { MapView(nearbyBusStops: viewModel.nearbyBusStops) .background( // 通过背景视图获取MapView实例引用 GeometryReader { _ in Color.clear.onAppear { self.mapViewRef = self.mapViewRef } } ) Button("显示公交站点") { mapViewRef?.addBusStopAnnotations() } } } }
方案2:通过ViewModel状态绑定触发
在ViewModel中添加@Published状态标记是否显示公交站点,然后在updateUIView中仅在状态变化时执行一次标注操作,避免重复执行:
class NearbyBusStopsViewModel: ObservableObject { @Published var nearbyBusStops: [BusStop] = [] @Published var shouldShowBusStops = false // 控制显示/隐藏的状态 private var hasLoadedAnnotations = false // 标记是否已添加过标注,避免重复执行 } struct MapView: UIViewRepresentable { @ObservedObject var viewModel: NearbyBusStopsViewModel func makeUIView(context: Context) -> MKMapView { MKMapView() } func updateUIView(_ uiView: MKMapView, context: Context) { if viewModel.shouldShowBusStops && !viewModel.hasLoadedAnnotations { // 移除旧的公交标注 let busAnnotations = uiView.annotations.filter { $0 is BusStopAnnotation } uiView.removeAnnotations(busAnnotations) // 创建并添加新标注 let newAnnotations = viewModel.nearbyBusStops.map { BusStopAnnotation(busStop: $0) } uiView.addAnnotations(newAnnotations) viewModel.hasLoadedAnnotations = true // 更新标记 } else if !viewModel.shouldShowBusStops && viewModel.hasLoadedAnnotations { // 隐藏时移除公交标注 let busAnnotations = uiView.annotations.filter { $0 is BusStopAnnotation } uiView.removeAnnotations(busAnnotations) viewModel.hasLoadedAnnotations = false } } } // 按钮点击时仅修改ViewModel状态 Button("切换公交站点显示") { viewModel.shouldShowBusStops.toggle() }
额外优化点
- 避免调用
uiView.removeAnnotations(uiView.annotations),这会移除所有标注(包括地图自带的用户位置标注),应该只过滤移除BusStopAnnotation类型的标注 - 如果公交站点数据量较大,可以在后台线程提前创建好所有
BusStopAnnotation实例,再回到主线程调用addAnnotations,减少主线程阻塞
内容的提问来源于stack exchange,提问作者User95797654974
相关产品推荐
相关产品推荐

