如何将UIViewRepresentable封装的MapView中的地址传递到SwiftUI视图显示
实现方案
核心通过SwiftUI的@Binding属性做跨层值同步,不需要主动读取Coordinator的属性,直接通过绑定将地址实时同步到上层视图即可。
修改步骤
- 给MapView新增地址绑定属性
在MapView的属性定义中新增用于接收上层绑定的地址变量 - 替换Coordinator中的地址赋值逻辑
拿到反向地理编码结果后直接给绑定变量赋值,自动触发上层视图刷新 - 上层视图新增地址状态变量,绑定到MapView后直接在UI中展示
完整修改后代码
MapView实现代码
struct MapView: UIViewRepresentable { @Binding var centerCoordinate: CLLocationCoordinate2D // 新增地址绑定属性 @Binding var address: String var currentLocation: CLLocationCoordinate2D? var withAnnotation: MKPointAnnotation? class Coordinator: NSObject, MKMapViewDelegate { var parent: MapView init(_ parent: MapView) { self.parent = parent } func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) { if !mapView.showsUserLocation { parent.centerCoordinate = mapView.centerCoordinate } } func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool){ let center = getCenterLocation(for: mapView) let geoCoder = CLGeocoder() geoCoder.reverseGeocodeLocation(center) { [weak self] (placemarks, error) in guard let self = self else { return } if let _ = error { DispatchQueue.main.async { self.parent.address = "地址获取失败" } print("error") return } guard let placemark = placemarks?.first else { DispatchQueue.main.async { self.parent.address = "未找到对应地址" } return } let streetNumber = placemark.subThoroughfare ?? "" let streetName = placemark.thoroughfare ?? "" let city = placemark.locality ?? "" // 可根据需求拼接更完整的地址信息 DispatchQueue.main.async { // 直接给parent的绑定地址赋值 self.parent.address = "\(city) \(streetName) \(streetNumber)" } } } // 补全缺失的getCenterLocation方法 func getCenterLocation(for mapView: MKMapView) -> CLLocation { let latitude = mapView.centerCoordinate.latitude let longitude = mapView.centerCoordinate.longitude return CLLocation(latitude: latitude, longitude: longitude) } } func makeCoordinator() -> Coordinator { Coordinator(self) } func makeUIView(context: Context) -> MKMapView { let mapView = MKMapView() mapView.delegate = context.coordinator mapView.showsUserLocation = false return mapView } func updateUIView(_ uiView: MKMapView, context: Context) { if let currentLocation = self.currentLocation { if let annotation = self.withAnnotation { uiView.removeAnnotation(annotation) } uiView.showsUserLocation = true let region = MKCoordinateRegion(center: currentLocation, latitudinalMeters: 1000, longitudinalMeters: 1000) uiView.setRegion(region, animated: true) } else if let annotation = self.withAnnotation { uiView.removeAnnotations(uiView.annotations) uiView.addAnnotation(annotation) } } }
上层SwiftUI视图代码
import SwiftUI import MapKit fileprivate let locationFetcher = LocationFetcher() struct LocationView: View { @State var centerCoordinate = CLLocationCoordinate2D() @State var currentLocation: CLLocationCoordinate2D? @State var annotation: MKPointAnnotation? // 新增地址状态变量 @State private var currentAddress = "正在定位..." var body: some View { ZStack { // 传入地址绑定 MapView(centerCoordinate: $centerCoordinate, currentLocation: currentLocation, withAnnotation: annotation, address: $currentAddress) .edgesIgnoringSafeArea([.leading, .trailing, .bottom]) .onAppear(perform: { locationFetcher.start() // 定位成功后给currentLocation赋值即可触发地图移动到当前位置 if let location = locationFetcher.lastKnownLocation { currentLocation = location.coordinate } }) // 固定中心标记,提升用户体验 Image(systemName: "mappin.and.ellipse") .font(.title) .foregroundColor(.red) } .overlay( ZStack { RoundedRectangle(cornerRadius: 8) .fill(.white) .frame(height: 44) .padding(.horizontal, 16) // 直接显示绑定的地址变量 Text(currentAddress) .font(.subheadline) } .offset(y: 44) ) } } struct LocationView_Previews: PreviewProvider { static var previews: some View { LocationView() } }
优化建议
- 可以给反向地理编码请求加防抖,比如延迟300ms再执行请求,避免用户拖动地图时频繁调用接口触发苹果的接口限频
- 地址拼接可根据需求补充行政区、门牌号等更详细的信息
- 错误状态可以配合弹窗提示用户
内容的提问来源于stack exchange,提问作者Oleg Savelyev
相关产品推荐
相关产品推荐

