ios 如何使用Apple Map Kit实现地址自动补全

w80xi6nr  于 2022-12-15  发布在  iOS
关注(0)|答案(5)|浏览(136)

我想自动完成的地址为用户一样,谷歌API在此链接中提供:
https://developers.google.com/maps/documentation/javascript/places-autocomplete?hl=en
我如何使用苹果Map工具包实现相同的功能?
我试过使用地理编码器,我写了这个例子:

@IBAction func SubmitGeoCode(sender: AnyObject) {

    let address = "1 Mart"
    let coder = CLGeocoder()

    coder.geocodeAddressString(address) { (placemarks, error) -> Void in

        for placemark in placemarks! {

            let lines = placemark.addressDictionary?["FormattedAddressLines"] as? [String]

            for addressline in lines! {
                print(addressline)
            }
        }
    }
}

然而,结果令人非常失望。
有没有苹果API可以实现这样的功能,或者我应该去谷歌API?
谢谢

kuarbcqp

kuarbcqp1#

更新-我使用Swift 3创建了一个简单的示例项目here,因为原始答案是用Swift 2编写的。

在iOS 9.3中引入了一个名为MKLocalSearchCompleter的新类,这允许创建一个自动完成解决方案,您只需按如下所示传入queryFragment:

var searchCompleter = MKLocalSearchCompleter()
searchCompleter.delegate = self
var searchResults = [MKLocalSearchCompletion]()

searchCompleter.queryFragment = searchField.text!

然后使用MKLocalSearchCompleterDelegate处理查询结果:

extension SearchViewController: MKLocalSearchCompleterDelegate {

    func completerDidUpdateResults(completer: MKLocalSearchCompleter) {
        searchResults = completer.results
        searchResultsTableView.reloadData()
    } 

    func completer(completer: MKLocalSearchCompleter, didFailWithError error: NSError) {
        // handle error
    }
}

并以适当的格式显示地址结果:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let searchResult = searchResults[indexPath.row]
    let cell = UITableViewCell(style: .subtitle, reuseIdentifier: nil)
    cell.textLabel?.text = searchResult.title
    cell.detailTextLabel?.text = searchResult.subtitle
    return cell
}

然后,您可以使用MKLocalCompletion对象示例化MKLocalSearch.Request,从而获得对MKPlacemark和所有其他有用数据的访问:

let searchRequest = MKLocalSearch.Request(completion: completion!)
let search = MKLocalSearch(request: searchRequest)
search.startWithCompletionHandler { (response, error) in
    if error == nil {
        let coordinate = response?.mapItems[0].placemark.coordinate
    }
}
vsmadaxz

vsmadaxz2#

Swift 5 +合并+(可选)SwiftUI解决方案

似乎有很多关于其他解决方案的评论想要一个与更新版本的Swift兼容的版本。另外,看起来很可能(正如我所做的),人们也将需要SwiftUI解决方案。
这是基于以前的建议,但使用“合并”来监视输入、消除反跳,然后通过Publisher提供结果。
MapSearchObservableObject可轻松用于SwiftUI(提供示例),但也可用于非SwiftUI情况。

Map搜索可观察对象

import SwiftUI
import Combine
import MapKit

class MapSearch : NSObject, ObservableObject {
    @Published var locationResults : [MKLocalSearchCompletion] = []
    @Published var searchTerm = ""
    
    private var cancellables : Set<AnyCancellable> = []
    
    private var searchCompleter = MKLocalSearchCompleter()
    private var currentPromise : ((Result<[MKLocalSearchCompletion], Error>) -> Void)?
    
    override init() {
        super.init()
        searchCompleter.delegate = self
        
        $searchTerm
            .debounce(for: .seconds(0.5), scheduler: RunLoop.main)
            .removeDuplicates()
            .flatMap({ (currentSearchTerm) in
                self.searchTermToResults(searchTerm: currentSearchTerm)
            })
            .sink(receiveCompletion: { (completion) in
                //handle error
            }, receiveValue: { (results) in
                self.locationResults = results
            })
            .store(in: &cancellables)
    }
    
    func searchTermToResults(searchTerm: String) -> Future<[MKLocalSearchCompletion], Error> {
        Future { promise in
            self.searchCompleter.queryFragment = searchTerm
            self.currentPromise = promise
        }
    }
}

extension MapSearch : MKLocalSearchCompleterDelegate {
    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
            currentPromise?(.success(completer.results))
        }
    
    func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
        //could deal with the error here, but beware that it will finish the Combine publisher stream
        //currentPromise?(.failure(error))
    }
}

SwiftUI界面,包括Map位置

struct ContentView: View {
    @StateObject private var mapSearch = MapSearch()
    
    var body: some View {
        NavigationView {
            Form {
                Section {
                    TextField("Address", text: $mapSearch.searchTerm)
                }
                Section {
                    ForEach(mapSearch.locationResults, id: \.self) { location in
                        NavigationLink(destination: Detail(locationResult: location)) {
                            VStack(alignment: .leading) {
                                Text(location.title)
                                Text(location.subtitle)
                                    .font(.system(.caption))
                            }
                        }
                    }
                }
            }.navigationTitle(Text("Address search"))
        }
    }
}

class DetailViewModel : ObservableObject {
    @Published var isLoading = true
    @Published private var coordinate : CLLocationCoordinate2D?
    @Published var region: MKCoordinateRegion = MKCoordinateRegion()
    
    var coordinateForMap : CLLocationCoordinate2D {
        coordinate ?? CLLocationCoordinate2D()
    }
    
    func reconcileLocation(location: MKLocalSearchCompletion) {
        let searchRequest = MKLocalSearch.Request(completion: location)
        let search = MKLocalSearch(request: searchRequest)
        search.start { (response, error) in
            if error == nil, let coordinate = response?.mapItems.first?.placemark.coordinate {
                self.coordinate = coordinate
                self.region = MKCoordinateRegion(center: coordinate, span: MKCoordinateSpan(latitudeDelta: 0.03, longitudeDelta: 0.03))
                self.isLoading = false
            }
        }
    }
    
    func clear() {
        isLoading = true
    }
}

struct Detail : View {
    var locationResult : MKLocalSearchCompletion
    @StateObject private var viewModel = DetailViewModel()
    
    struct Marker: Identifiable {
        let id = UUID()
        var location: MapMarker
    }
    
    var body: some View {
        Group {
            if viewModel.isLoading {
                Text("Loading...")
            } else {
                Map(coordinateRegion: $viewModel.region,
                    annotationItems: [Marker(location: MapMarker(coordinate: viewModel.coordinateForMap))]) { (marker) in
                    marker.location
                }
            }
        }.onAppear {
            viewModel.reconcileLocation(location: locationResult)
        }.onDisappear {
            viewModel.clear()
        }
        .navigationTitle(Text(locationResult.title))
    }
}
2skhul33

2skhul333#

我的答案完全基于“乔治·麦克唐奈”。我希望这对那些在执行最后一个问题上遇到困难的人有所帮助。

import UIKit
import MapKit

class ViewController: UIViewController {

    @IBOutlet weak var searchBar: UISearchBar!
    @IBOutlet weak var tableVIew: UITableView!

    //create a completer
    lazy var searchCompleter: MKLocalSearchCompleter = {
        let sC = MKLocalSearchCompleter()
        sC.delegate = self
        return sC
    }()

    var searchSource: [String]?
}

extension ViewController: UISearchBarDelegate {
    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        //change searchCompleter depends on searchBar's text
        if !searchText.isEmpty {
            searchCompleter.queryFragment = searchText
        }
    }
}

extension ViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return searchSource?.count ?? 0
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //I've created SearchCell beforehand; it might be your cell type
        let cell = self.tableVIew.dequeueReusableCell(withIdentifier: "SearchCell", for: indexPath) as! SearchCell

        cell.label.text = self.searchSource?[indexPath.row]
//            + " " + searchResult.subtitle

        return cell
    }
}

extension ViewController: MKLocalSearchCompleterDelegate {
    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        //get result, transform it to our needs and fill our dataSource
        self.searchSource = completer.results.map { $0.title }
        DispatchQueue.main.async {
            self.tableVIew.reloadData()
        }
    }

    func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
        //handle the error
        print(error.localizedDescription)
    }
}

字符串

wsxa1bj1

wsxa1bj14#

此问题的示例项目可从HERE下载
在本示例项目中,此问题通过MKLocalSearchRequest和MapKit实现。

它显示自动完成的地方就像谷歌的地方API,可以把苹果的Map上的注解点(不是在谷歌Map上,我希望这是只有你正在寻找)。
然而,它并没有显示准确的结果,因为你可以从谷歌地点API。因为问题是,地理编码数据库显然不是完整的,苹果不是该领域的领导者-谷歌是。

附上示例应用程序的一些屏幕截图,以便您查看它是否对您的要求有用。

希望这是你要找的!

lndjwyie

lndjwyie5#

简单的解决方案- SwiftUI

方式:searchText链接到Textfield,当Textfield更改时,将根据全球地址查询(比较)searchText。

查询的完成触发了completerDidUpdateResults,它使用这些结果(地址)更新SearchThis.swift列表。

搜索此.swift(SwiftUI)

import SwiftUI
import Foundation

struct SearchThis : View {
    @StateObject var searchModel = SearchModel()
    
    var body: some View {
        VStack {
            TextField("Type Here", text: $searchModel.searchText)
                .onChange(of: searchModel.searchText) { newValue in
                    searchModel.completer.queryFragment = searchModel.searchText
                }
            List(searchModel.locationResult, id: \.self) { results in
                Button(results.title) {print("hi")}
            }
        }
    }
}

struct SearchThis_Previews: PreviewProvider {
    static var previews: some View {
        SearchThis()
    }
}

搜索模型.swift(类)

import MapKit

class SearchModel: NSObject, ObservableObject, MKLocalSearchCompleterDelegate {
    @Published var searchText = ""
    @Published var locationResult: [MKLocalSearchCompletion] = []
    
    
    let completer = MKLocalSearchCompleter()
    
    override init() {
        super.init()
        completer.delegate = self
    }

    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        locationResult = completer.results
    }

    func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
        print(error.localizedDescription)
    }
}

相关问题