我是 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
}
}
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
更改则取消并重新启动,并在消失时取消。您不能在视图模型对象中执行此操作。