如何将 MKMapItem 从视图传递到 SwiftUI 中的 Google 地图协调器?

问题描述 投票:0回答:1

我是 SwiftUI 新手,正在尝试创建一个基于 Google 地图的 iOS 应用程序,该应用程序将允许用户使用 MapKit 的搜索完成功能从“搜索”视图中搜索位置。选择位置将在“信息”视图上显示详细信息,并带有“保存”按钮,如果单击该按钮,则会在地图上添加标记。

我的问题是如何实现调用 Google 地图协调器中的 addLocation() 帮助程序的“保存”按钮的操作。

如果我将

@Published var locationToAdd: MKMapItem?
添加到 SearchLocationViewModel 并将
@EnvironmentObject var viewModel: SearchLocationViewModel
添加到 GoogleMapViewRepresentable ,它将调用 updateUIView() 并且我可以添加
if viewModel.locationToAdd
条件来添加标记,但是当我输入搜索或选择搜索完成结果,因为观察到的 SearchLocationViewModel 已更新。

如果我在 addLocation() 中添加标记后立即设置

viewModel.locationToAdd = nil
,则会收到错误(nil 无法初始化指定类型 MKMapItem),但即使我没有这样做,也会触发对 SearchLocationViewModel 和另一个 updateUIView( )打电话(我不确定这是否会成为问题,但看起来很草率)。

创建另一个仅包含单个

@Published var locationToAdd: MKMapItem?
的视图模型并将其与 SearchLocationViewModel 分开是正确的解决方案吗?

相关代码:

GoogleMapView可表示:

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)")
        }
    }}
}

搜索位置视图模型:

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
    }
}
ios swiftui mapkit google-maps-sdk-ios
1个回答
0
投票

makeUIView
需要制作,例如

    //let mapView = GMSMapView(frame: .zero)

    func makeUIView(context: Context) -> GMSMapView {
        return GMSMapView(frame: .zero) // mapView
    }

self
是一个值,而不是引用,因此您需要修复协调器初始化,例如

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

我们在 SwiftUI 中不使用视图模型对象,您需要学习

View
结构体的功能,例如进行异步搜索,如下所示:

struct SearchView: View {

    let query: String

    @State var results: [ResultType]

    var body: some View {
        ...
        .task(id: query) {
            if query == "" {
                results = []
                return
            }
            let localSearch = ..
            let response = await localSearch.start()
            results = ...
        }
    }

任务在出现时启动,如果

id
更改则取消并重新启动,并在消失时取消。您不能在视图模型对象中执行此操作。

© www.soinside.com 2019 - 2024. All rights reserved.