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

如何将UIViewRepresentable封装的MapView中的地址传递到SwiftUI视图显示

实现方案

核心通过SwiftUI的@Binding属性做跨层值同步,不需要主动读取Coordinator的属性,直接通过绑定将地址实时同步到上层视图即可。

修改步骤

  1. 给MapView新增地址绑定属性
    在MapView的属性定义中新增用于接收上层绑定的地址变量
  2. 替换Coordinator中的地址赋值逻辑
    拿到反向地理编码结果后直接给绑定变量赋值,自动触发上层视图刷新
  3. 上层视图新增地址状态变量,绑定到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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 05:24:04