LoginSignup
28
22

More than 5 years have passed since last update.

iOS9.3で追加されたMKLocalSearchCompleterを試す

Posted at

はじめに

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のデリゲートを自身に設定します。こいつは検索結果がアップデートされる(またはエラー)タイミングで何か処理をさせることができます。

またfilterTypeLocationsAndQueriesLocationsOnlyがあり、前者は場所の名前と一般的な用語(ex. Cof -> Coffee)も一緒に補完されます。後者は場所の名前のみです。

ViewController.swift
    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.

ViewController.swift
    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:を呼んでくれるので、ここにテーブルのリロード処理を入れておけば検索結果が常時反映されます。

ViewController.swift
    func completerDidUpdateResults(completer: MKLocalSearchCompleter) {

        tableView.reloadData()
    }

completer.resultsに検索結果の[MKLocalSearchCompletion]が入っているので、それぞれ取り出してセルのテキストラベルに入れればおしまいです。

ViewController.swift
    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!
    }

スクリーンショット 2016-03-30 11.28.02.png

マップにアノテーションとして追加

ローカル検索のリクエストMKLocalSearchRequestのイニシャライザにMKLocalSearchCompletionから生成できるものが追加されたのでこれを使います。

ViewController.swift
    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)
    }

あとは受け取ったマップ画面でローカル検索をするだけです。ちょっと便利になりましたね。

MapViewController.swift
    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)
        }
    }

スクリーンショット 2016-03-30 11.29.24.png

28
22
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
22