MKLocalSearchCompleter を使用して検索を完了し、ユーザーが場所を入力できるようにするテキストフィールドを備えたアプリを作成しようとしました。その後、座標を取得してMapKitに表示したいと思います。ただし、Geocoder を使用して座標を取得できませんでした。
class LocationSearchService: NSObject, ObservableObject, MKLocalSearchCompleterDelegate {
@Published var searchQuery = ""
var completer: MKLocalSearchCompleter
@Published var completions: [MKLocalSearchCompletion] = []
var cancellable: AnyCancellable?
override init() {
completer = MKLocalSearchCompleter()
super.init()
cancellable = $searchQuery.assign(to: \.queryFragment, on: self.completer)
completer.delegate = self
}
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
self.completions = completer.results
}
}
ロケーションマネージャーは次のとおりです。
class LocationManager: NSObject, ObservableObject {
private let locationManager = CLLocationManager()
private let geocoder = CLGeocoder()
let objectWillChange = PassthroughSubject<Void, Never>()
@Published var status: CLAuthorizationStatus? {
willSet { objectWillChange.send() }
}
@Published var location: CLLocation? {
willSet { objectWillChange.send() }
}
override init() {
super.init()
self.locationManager.delegate = self
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
}
@Published var placemark: CLPlacemark? {
willSet { objectWillChange.send() }
}
private func lookupLocation() {
guard let location = self.location else { return }
geocoder.reverseGeocodeLocation(location, completionHandler: { (places, error) in
if error == nil {
self.placemark = places?[0]
} else {
self.placemark = nil
}
})
}
// !!! This is the function I would like to use to get the Coordinate from the address obtained from LocationSearchService
func getCoordinate(address: String) {
geocoder.geocodeAddressString(address, completionHandler: { (places, error) in
if error == nil {
self.placemark = places?[0]
self.location = self.placemark?.location
} else {
self.placemark = nil
self.location = nil
}
})
}
}
extension LocationManager: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
self.status = status
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return } //.first or .last?
self.location = location
self.lookupLocation()
}
}
次のようなコンテンツ ビュー:
struct ContentView: View {
@State private var location: String = ""
@ObservedObject var lm = LocationManager()
private let completer = MKLocalSearchCompleter()
@ObservedObject var locationSearchService = LocationSearchService()
var body: some View {
NavigationView {
VStack {
AddressSearchBar(text: $locationSearchService.searchQuery)
List(locationSearchService.completions, id: \.self) { completion in
VStack(alignment: .leading) {
Text(completion.title)
// Error here, I cannot translate the address to location
//Text(lm.getCoordinate(address: completion.title))
}
}.navigationTitle("Search Location")
}
}
ここでいくつかの問題:
- ユーザーが選択したアイテム (ここでは実装できませんでした) をアドレス (completion.title) に変換したいと思います。つまり、提案されたアイテムをユーザーが選択する必要があります。
- 提案で見つかった住所を座標に変換して、MapView でマークできるようにしたいと思います。