52
43

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

<Swift> MKLocalSearchでプレイス検索

Last updated at Posted at 2016-07-14

TL;DR

iOS標準Mapkitには、実は、自然言語(例:コンビニ 新宿)でプレイス検索ができるMKLocalSearchが存在します!
Google Places APIほどの精度はありませんが、クレカ登録などの面倒な手続きを踏まずに利用できる便利なAPIです。
その実装方法を紹介します。Swift 4.2です。

更新

  • 2019/02/14 コードの整理とSwift 4.2対応

準備

まずMKPlacemarkを拡張します。

MKPlacemarkは、都道府県、市区町村、番地などのプロパティを持っていますが、
「東京都渋谷区千駄ヶ谷1-2-3」のような出力をするには、これらひとつひとつ連結しなくてはなりません。

そこでaddressというComputed Propertyを持たせます。

import MapKit
extension MKPlacemark {
    var address: String {
        let components = [self.administrativeArea, self.locality, self.thoroughfare, self.subThoroughfare]
        return components.compactMap { $0 }.joined(separator: "")
    }
}

英語の場合は、↓の表記ですね。

return components.reversed().compactMap{ $0 }.joined(separator: ",")

検索メソッド

ここで用意した引数は2つです。
queryには自然言語(例:コンビニ 新宿)を渡し、regionにはMKCoordinateRegionを渡すとヒントとして利用され、検索精度の向上が見込めます(任意)。

struct Map {
    enum Result<T> {
        case success(T)
        case failure(Error)
    }

    static func search(query: String, region: MKCoordinateRegion? = nil, completionHandler: @escaping (Result<[MKMapItem]>) -> Void) {
        let request = MKLocalSearch.Request()
        request.naturalLanguageQuery = query

        if let region = region {
            request.region = region
        }

        MKLocalSearch(request: request).start { (response, error) in
            if let error = error {
                completionHandler(.failure(error))
                return
            }
            completionHandler(.success(response?.mapItems ?? []))
        }
    }
}

基準となるRegionを渡し、検索実行!

検索には、座標と検索範囲のregionを設定します。

let coordinate = CLLocationCoordinate2DMake(35.6598051, 139.7036661) // 渋谷ヒカリエ
let region = MKCoordinateRegion(center: coordinate, latitudinalMeters: 1000.0, longitudinalMeters: 1000.0) // 1km * 1km

Map.search(query: "コンビニ", region: region) { (result) in
    switch result {
    case .success(let mapItems):
        for map in mapItems {
            print("name: \(map.name ?? "no name")")
            print("coordinate: \(map.placemark.coordinate.latitude) \(map.placemark.coordinate.latitude)")
            print("address \(map.placemark.address)")
        }
    case .failure(let error):
        print("error \(error.localizedDescription)")
    }
}

結果

[MKMapItem]が検索結果として返ってきます

#map name: ローソン渋谷三丁目店
#map coordinate: 35.6560398 35.6560398
#map address 東京都渋谷区渋谷3丁目28番8号
#map name: ファミリーマート麹町二丁目店
#map coordinate: 35.6834507 35.6834507
#map address 東京都千代田区麹町2丁目5番4号
#map name: ナチュラルローソン&フードクルック 神宮外苑西店
#map coordinate: 35.671825 35.671825
#map address 東京都渋谷区神宮前3丁目35番8号

あとは、Mapにannotationを立てるなり、Cellに表示するなりしてください!

52
43
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
52
43

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?