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

SwiftUI中如何将MKMapItem传递至Google Maps协调器?

问题:如何在SwiftUI中调用Google Maps协调器的addLocation()方法

我是SwiftUI新手,正在开发一款基于Google Maps的iOS应用,流程如下:用户在「搜索」视图中使用MapKit的搜索补全功能查找地点,选中地点后「信息」视图会展示详情,点击其中的保存按钮可在地图上添加标记。由于Google Maps不支持SwiftUI,部分代码使用Swift编写。

核心问题:如何实现保存按钮的动作,使其调用Google Maps协调器中的addLocation()辅助方法。

已尝试方案的问题

  • 方案一:在SearchLocationViewModel中添加@Published var locationToAdd: MKMapItem?,并在GoogleMapViewRepresentable中注入@EnvironmentObject var viewModel: SearchLocationViewModel,在updateUIView()中通过if viewModel.locationToAdd条件添加标记。但输入搜索内容或选中搜索补全结果时,SearchLocationViewModel的更新会触发updateUIView(),导致不必要的地图视图刷新。
  • 方案二:在addLocation()添加标记后立即设置viewModel.locationToAdd = nil,但会出现错误(nil无法初始化指定类型MKMapItem);即便没有错误,也会触发SearchLocationViewModel的再次更新及updateUIView()调用,逻辑不够严谨。

正确解决方案:拆分独立的地图标记视图模型

是的,单独创建一个仅负责地图标记操作的视图模型,与SearchLocationViewModel分离,是更符合单一职责原则、逻辑更清晰的方案,能避免搜索操作的更新影响地图视图。

具体实现步骤

  1. 创建独立的MapMarkerViewModel
import MapKit

class MapMarkerViewModel: ObservableObject {
    @Published var locationToAdd: MKMapItem?
}
  1. 修改GoogleMapViewRepresentable,注入MapMarkerViewModel
import SwiftUI
import MapKit
import GoogleMaps

struct GoogleMapViewRepresentable: UIViewRepresentable {
    @EnvironmentObject var markerViewModel: MapMarkerViewModel
    let mapView = GMSMapView(frame: .zero)

    func makeCoordinator() -> MapCoordinator {
        return MapCoordinator(mapView: self)
    }

    func makeUIView(context: Context) -> GMSMapView {
        return mapView
    }
    
    func updateUIView(_ mapView: GMSMapView, context: Context) {
        guard let location = markerViewModel.locationToAdd else { return }
        // 调用协调器的添加方法
        context.coordinator.addLocation(location)
        // 处理后重置,避免重复触发更新
        markerViewModel.locationToAdd = nil
    }
}

extension GoogleMapViewRepresentable {
    class MapCoordinator: NSObject, CLLocationManagerDelegate {
        var parent: GoogleMapViewRepresentable
        
        init(mapView: GoogleMapViewRepresentable) {
            parent = mapView
        }
        
        func addLocation(_ item: MKMapItem) {
            // 实现添加Google Maps标记的具体逻辑
            let coordinate = item.placemark.coordinate
            let marker = GMSMarker(position: coordinate)
            marker.title = item.name
            marker.snippet = item.placemark.title
            marker.map = parent.mapView
            print("添加地点标记: \(item.name ?? "未知地点")")
        }
    }
}
  1. 在保存按钮动作中设置标记位置
    在展示地点详情的「信息」视图中,注入MapMarkerViewModel,并在保存按钮点击时传递选中的地点:
struct InfoView: View {
    @EnvironmentObject var viewModel: SearchLocationViewModel
    @EnvironmentObject var markerViewModel: MapMarkerViewModel
    
    var body: some View {
        VStack {
            // 展示地点详情
            Text(viewModel.selectedLocationResponseItem?.name ?? "无地点信息")
            Button("保存地点") {
                if let selectedItem = viewModel.selectedLocationResponseItem {
                    markerViewModel.locationToAdd = selectedItem
                }
            }
        }
    }
}
  1. 在应用入口注入两个视图模型
    确保在父视图层级中同时注入两个ViewModel,让子视图可以通过EnvironmentObject访问:
@main
struct YourMapApp: App {
    let searchViewModel = SearchLocationViewModel()
    let markerViewModel = MapMarkerViewModel()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(searchViewModel)
                .environmentObject(markerViewModel)
        }
    }
}

方案优势

  • 职责分离:SearchLocationViewModel专注处理搜索补全逻辑,MapMarkerViewModel仅负责地图标记的添加操作,代码结构更清晰。
  • 避免无效刷新:只有当真正需要添加标记时,才会触发GoogleMapViewRepresentable的updateUIView(),搜索操作的更新不会影响地图视图。
  • 逻辑严谨:处理完标记添加后重置locationToAdd为nil,不会导致重复触发,且可选类型赋值nil合法,不会出现之前的类型错误。

原问题相关代码

GoogleMapViewRepresentable(原始版本)

import SwiftUI
import MapKit
import GoogleMaps

struct GoogleMapViewRepresentable: UIViewRepresentable {

    let mapView = GMSMapView(frame: .zero)

    func makeCoordinator() -> MapCoordinator {
        return MapCoordinator(mapView: self)
    }

    func makeUIView(context: Context) -> GMSMapView {
        return mapView
    }
    
    func updateUIView(_ mapView: GMSMapView, context: Context) {
    }
}

extension GoogleMapViewRepresentable {

    class MapCoordinator: NSObject, CLLocationManagerDelegate {
        var parent: GoogleMapViewRepresentable
        
        init(mapView: GoogleMapViewRepresentable) {
            parent = mapView
        }
        
        func addLocation(_ item: MKMapItem?) {
            print("Item: \(item)")
        }
    }}

SearchLocationViewModel(原始版本)

import MapKit

class SearchLocationViewModel: NSObject, ObservableObject {

    @Published var results = [MKLocalSearchCompletion]()
    @Published var selectedLocationResponseItem: MKMapItem?
    private let searchCompleter = MKLocalSearchCompleter()
    var queryFragment: String = "" {
        didSet {
            searchCompleter.queryFragment = queryFragment
        }
    }

    override init() {
        super.init()
        searchCompleter.delegate = self
        searchCompleter.queryFragment = queryFragment
        searchCompleter.resultTypes = MKLocalSearchCompleter.ResultType([.address, .pointOfInterest])
    }

    func searchLocation(forLocalSearchCompletion completion: MKLocalSearchCompletion, completionHandler: @escaping MKLocalSearch.CompletionHandler) {
        
        let searchRequest = MKLocalSearch.Request(completion: completion)
        let search = MKLocalSearch(request: searchRequest)
        search.start(completionHandler: completionHandler)
    }

    func selectLocation(_ localSearch: MKLocalSearchCompletion) {
        searchLocation(forLocalSearchCompletion: localSearch) { response, error in
            
            if let error = error {
                print("Error: \(error.localizedDescription)")
                return
            }

            guard let item = response?.mapItems.first else { return }
            self.selectedLocationResponseItem = item
        }
    }
}

extension SearchLocationViewModel: MKLocalSearchCompleterDelegate {
    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        self.results = completer.results
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 20:55:56