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

地图初始加载时Annotation不显示,移动地图后才出现的问题求助

问题描述

我开发的地图通过Google API加载Annotation,控制台打印显示初始加载时已完成Annotation放置,但需移动一次地图后,Annotation才会在地图上显示。请问是否需要调用方法在放置Annotation后更新地图?

相关代码

ContentView.swift

struct ContentView: View {
    
    var locationSearch = LocationSearch()
    @State private var mapView = MapView()
    @State var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: -33.7944, longitude: 151.2649), span: MKCoordinateSpan(latitudeDelta: 0.015, longitudeDelta: 0.015))
    @EnvironmentObject var sheetManager: SheetManager

    var body: some View {
        mapView
            .popup(with: SheetManager())
            .frame(width: UIScreen.screenWidth, height: UIScreen.screenHeight)
    }
    
}

MapView.swift

struct MapView: UIViewRepresentable {
    @State var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: -33.7944, longitude: 151.2649), span: MKCoordinateSpan(latitudeDelta: 0.015, longitudeDelta: 0.015))
    
    func updateUIView(_ uiView: MKMapView, context: Context) {
        print("FLF: MapView updated")
        uiView.setNeedsDisplay()
    }
    
    
    var locationManager = CLLocationManager()
    let mapView = MKMapView(frame: CGRect(x: 0, y: 0, width: UIScreen.screenWidth, height: UIScreen.screenHeight))
    
    func setupManager() {
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestWhenInUseAuthorization()
        locationManager.requestAlwaysAuthorization()
    }
    
    func makeUIView(context: Context) -> MKMapView {
        setupManager()
        mapView.region = ContentView().region
        mapView.showsUserLocation = true
        mapView.userTrackingMode = .follow
        mapView.delegate = context.coordinator // set the delegate to the coordinator
        placeMarkersForRegion(region: region)
        return mapView
    }
    
    func placeMarkersForRegion(region: MKCoordinateRegion) {
        var locationSearch = LocationSearch()
        locationSearch.performSearch(region: region) { venues in
            print("FLF: Placing \(venues.count) marker(s)")
            for marker in venues {
                let annotation = MKPointAnnotation()
                annotation.coordinate = marker.location
                annotation.title = marker.name
                mapView.addAnnotation(annotation)
            }
        }
    }
    
    func makeCoordinator() -> MapViewCoordinator {
        MapViewCoordinator(self) // pass self to the coordinator so it can call `regionDidChangeAnimated`
    }

    func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
        // Use the 'coordinate' property to get the current location of the map view
        let currentRegion = mapView.region
        print("FLF: Map has moved")
        self.placeMarkersForRegion(region: currentRegion)
        // Do something with the current region (e.g. update a state variable or perform a search)
    }
    
}

MapViewCoordinator.swift

class MapViewCoordinator: NSObject, MKMapViewDelegate {
    var parent: MapView // add a property to hold a reference to the parent view

    init(_ parent: MapView) {
        self.parent = parent
    }

    func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
        // Call the parent's implementation of this method
        parent.mapView(mapView, regionDidChangeAnimated: animated)
    }

  func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
      view.canShowCallout = true
      view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)

          // Get the tapped annotation
          guard let annotation = view.annotation else { return }

          // Print the title of the annotation
      print(annotation.title ?? "Unknown")
  }

  func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
      print("FLF: Marker tapped")
  }
}

问题分析与解决方案

核心原因

  1. 异步回调不在主线程:performSearch是异步操作,回调默认可能在后台线程执行,而UIKit的MKMapView添加Annotation属于UI操作,必须在主线程执行,否则无法立即触发地图刷新。
  2. MapView实例属性的生命周期问题:你在MapView结构体中直接声明了let mapView = MKMapView(...)作为实例属性,而UIViewRepresentable的正确做法是在makeUIView方法内创建地图实例,避免生命周期不匹配导致的渲染延迟。

具体修复步骤

1. 确保在主线程添加Annotation

修改placeMarkersForRegion方法,将添加Annotation的代码包裹在DispatchQueue.main.async中:

func placeMarkersForRegion(region: MKCoordinateRegion) {
    var locationSearch = LocationSearch()
    locationSearch.performSearch(region: region) { venues in
        print("FLF: Placing \(venues.count) marker(s)")
        // 切换到主线程执行UI操作
        DispatchQueue.main.async {
            for marker in venues {
                let annotation = MKPointAnnotation()
                annotation.coordinate = marker.location
                annotation.title = marker.name
                self.mapView.addAnnotation(annotation)
            }
            // 可选:手动触发地图刷新(如果主线程操作后仍不显示)
            self.mapView.setNeedsDisplay()
        }
    }
}

2. 修正MapView实例的创建方式

删除MapView结构体中的let mapView = MKMapView(...)实例属性,改为在makeUIView内创建:

struct MapView: UIViewRepresentable {
    @State var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: -33.7944, longitude: 151.2649), span: MKCoordinateSpan(latitudeDelta: 0.015, longitudeDelta: 0.015))
    
    func updateUIView(_ uiView: MKMapView, context: Context) {
        print("FLF: MapView updated")
        uiView.setNeedsDisplay()
    }
    
    var locationManager = CLLocationManager()
    
    func setupManager() {
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.requestWhenInUseAuthorization()
        locationManager.requestAlwaysAuthorization()
    }
    
    func makeUIView(context: Context) -> MKMapView {
        setupManager()
        // 在makeUIView内创建地图实例
        let mapView = MKMapView(frame: UIScreen.main.bounds)
        mapView.region = self.region // 改用自身的@State region,避免创建ContentView实例
        mapView.showsUserLocation = true
        mapView.userTrackingMode = .follow
        mapView.delegate = context.coordinator
        placeMarkersForRegion(region: region)
        return mapView
    }
    
    // ... 其他方法保持不变
}

3. 优化region传递逻辑(可选)

原代码中mapView.region = ContentView().region会创建新的ContentView实例,导致region值可能不一致,改为直接使用self.region,如果需要和ContentView的region同步,可以将region改为@Binding属性:

// MapView结构体修改
struct MapView: UIViewRepresentable {
    @Binding var region: MKCoordinateRegion // 改为Binding
    // ... 其他代码
}

// ContentView中传递region
struct ContentView: View {
    // ...
    var body: some View {
        MapView(region: $region) // 传递绑定
            .popup(with: SheetManager())
            .frame(width: UIScreen.screenWidth, height: UIScreen.screenHeight)
    }
}

结论

不需要额外调用特殊的地图更新方法,只要保证在主线程添加Annotation,并遵循UIViewRepresentable的生命周期规范创建地图实例,Annotation就能在初始加载时正常显示。如果仍有问题,可以在主线程添加完Annotation后调用mapView.setNeedsDisplay()强制触发刷新。

内容的提问来源于stack exchange,提问作者MirrorJacket123

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 14:35:11