はじめに
iOS9.3で追加されたMKLocalSearchCompleter
がちょっと便利だったので、どんな風に使えばいいのかと ローカル検索(MKLocalSearch
)と合わせて使う方法をサンプルコードを用いて解説します。ローカル検索がなんだかわからない人はコチラが参考になるかと思います。
実行環境
- iOS: Version 9.3
- Swift: Version 2.2
- Xcode: Version 7.3
MKLocalSearchCompleterとは
最初ドキュメントをパッと見たときにはローカル検索と何が違うんだろうと思ってしまったのですが、よく読むとこいつは検索結果をあらかじめ確認してプロットさせたい時などに使える、クエリの補完機能を提供するものでした。
An MKLocalSearchCompleter object takes a partial search string and generates a list of potential completions.
今までありきたりな名前のお店を検索した時などに、求めていない場所まで地図にプロットされていたものを事前にユーザに絞らせてから検索させることができます。
サンプルコード
Appleが提供しているマップのように、検索窓に文字列を入力するタイミングで候補となる場所をテーブルビューに表示させるサンプルを作りました。一応ここにもあげておきます。
テーブルビューに場所の候補を表示
MKLocalSearchCompleterDelegate
のデリゲートを自身に設定します。こいつは検索結果がアップデートされる(またはエラー)タイミングで何か処理をさせることができます。
またfilterType
はLocationsAndQueries
とLocationsOnly
があり、前者は場所の名前と一般的な用語(ex. Cof -> Coffee)も一緒に補完されます。後者は場所の名前のみです。
override func viewDidLoad() {
super.viewDidLoad()
completer.delegate = self
completer.filterType = .LocationsOnly
}
検索窓のshouldChangeTextInRange
で入力された文字をqueryFragment
に突っ込んでいます。こいつに入った段階で自動的に非同期で検索が行われているようです。適度にユーザの入力がされてから検索させる為にディレイもしてくれているみたいです。
The completer object waits a short amount of time before initiating new searches. This delay gives you enough time to update the search string based on typed input from the user.
func searchBar(searchBar: UISearchBar, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
guard let text = searchBar.text else { return false }
completer.queryFragment = text
return true
}
先ほど設定したMKLocalSearchCompleterDelegate
が検索完了時に completerDidUpdateResults:
を呼んでくれるので、ここにテーブルのリロード処理を入れておけば検索結果が常時反映されます。
func completerDidUpdateResults(completer: MKLocalSearchCompleter) {
tableView.reloadData()
}
completer.results
に検索結果の[MKLocalSearchCompletion]
が入っているので、それぞれ取り出してセルのテキストラベルに入れればおしまいです。
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let identifier = "Cell"
var cell = tableView.dequeueReusableCellWithIdentifier(identifier)
if (cell == nil) {
cell = UITableViewCell(style: .Default, reuseIdentifier: identifier)
}
cell?.textLabel?.text = completer.results[indexPath.row].title
return cell!
}
マップにアノテーションとして追加
ローカル検索のリクエストMKLocalSearchRequest
のイニシャライザにMKLocalSearchCompletion
から生成できるものが追加されたのでこれを使います。
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let mapViewController = storyboard.instantiateViewControllerWithIdentifier("MapViewController") as? MapViewController else { return }
mapViewController.request = MKLocalSearchRequest(completion: completer.results[indexPath.row])
self.showViewController(mapViewController, sender: nil)
}
あとは受け取ったマップ画面でローカル検索をするだけです。ちょっと便利になりましたね。
override func viewDidLoad() {
super.viewDidLoad()
let search = MKLocalSearch(request: request)
search.startWithCompletionHandler { response, error in
response?.mapItems.forEach { item in
let point = MKPointAnnotation()
point.coordinate = item.placemark.coordinate
point.title = item.placemark.title
self.mapView.addAnnotation(point)
}
self.mapView.showAnnotations(self.mapView.annotations, animated: true)
}
}