【Swift】CLGeocoderの使い方。住所を座標に変換してピンを刺す。(Swift 2.1、XCode 7.2)
地図の使われ方で一番多いものは
前回までの記事で、MKMapView(以下、マップビュー)を使って地図を表示し、指定した座標にピンを刺す方法を説明してきた。⇒「記事」
目的地を探すときに、座標を入力したり、地図をスクロールして探すような使い方はあまりされない。一番多いのは、住所やキーワードから目的地を検索する使い方だ。そこで本記事では、住所を座標に変換してピンを刺す方法と、ピンを刺した座標を住所に変換する方法を説明する。
以降の手順は「Map Kit Viewの使い方」の続きから行うので、実装を試してみる人は先に読んでおくことをお勧めする。
住所を座標に変換してピンを刺す
テキストフィールドに入力した住所を緯度経度の座標に変換し、その座標にピンを刺す動きを実装する。
まず、デバイス画面にテキストフィールドを配置する(下図赤矢印)。黄緑枠のPinボタンを押して吹き出しの設定画面を表示し、Constrain to marginsのチェックを外す。
黄枠の位置の制約に上「90」、左「30」、右「30」(下は入力しない)、水色枠のHeightにチェックを入れて「Add 4 Constrains」ボタンを押す。これで、画面上部にテキストフィールドが表示されるようになった。
下図赤枠のアシスタントエディタボタンを押してViewController.swiftを開く。
Ctrlキーを押しながらテキストフィールドをドラッグ&ドロップでソースコードまで運んで吹き出しの設定画面を表示する。Connectionに「Outlet」、Nameに「testTextField」を入力し、Connectボタンを押す。これでテキストフィールドをソースコードで操作できるようになった。
ViewController.swiftを以下のコードに変更する。
キーボードのReturnキーが押されたら、テキストフィールドの文字列を使って住所を座標に変換している。
変換にはCLGeocoderクラスのgeocodeAddressStringメソッドを利用し、引数には「住所」と「クロージャ」を与える。「クロージャ」には変換結果とエラー情報の2つの変数が渡ってくるので、エラー情報に何も設定されていないときは変換成功としてピンを刺す。
クロージャについては次の記事を参照されたし。⇒「クロージャとは」
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
// // ViewController.swift // import UIKit import MapKit class ViewController: UIViewController,UITextFieldDelegate { @IBOutlet weak var testMapView: MKMapView! @IBOutlet weak var testTextField: UITextField! //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() //デリゲート先に自分を設定する。 testTextField.delegate = self } //Returnキー押下時の呼び出しメソッド func textFieldShouldReturn(textField: UITextField) -> Bool { self.view.endEditing(true) let myGeocoder:CLGeocoder = CLGeocoder() //住所 let searchStr = testTextField.text //住所を座標に変換する。 myGeocoder.geocodeAddressString(searchStr!, completionHandler: {(placemarks, error) in if(error == nil) { for placemark in placemarks! { let location:CLLocation = placemark.location! //中心座標 let center = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude) //表示範囲 let span = MKCoordinateSpanMake(0.001, 0.001) //中心座標と表示範囲をマップに登録する。 let region = MKCoordinateRegionMake(center, span) self.testMapView.setRegion(region, animated:true) //地図にピンを立てる。 let annotation = MKPointAnnotation() annotation.coordinate = CLLocationCoordinate2DMake(location.coordinate.latitude, location.coordinate.longitude) self.testMapView.addAnnotation(annotation) } } else { self.testTextField.text = "検索できませんでした。" } }) //改行を入れない。 return false } } |
以下は実際のプレイ動画。キーボードで住所を入力するのに時間がかかるので、前もってコピーしておいた文字列をペーストしている。
座標を住所に変換する
次は、地図を長押しした座標を住所に変換し、吹き出しのサブタイトルに設定する動きを実装する。
Long Press Gesture Recognizer(以下、長押し検知)をデバイス画面に配置する(下図赤矢印)。黄緑枠の長押し検知が追加されるので、Ctrlキーを押しながら長押し検知をドラッグ&ドロップでソースコードまで運んで吹き出しの設定画面を表示させる(青矢印)。
Connectionに「Action」、Nameに「pressMap」、Typeに「UILongPressGestureRecognizer」を設定してConnectボタンを押す。これで地図が長押しされたときのイベントをソースコードで受けれるようになった。
ViewController.swiftを以下のコードに変更する。
長押しされた地図の場所を緯度経度の座標に変換したあと、先ほどとは逆方向のreverseGeocodeLocationメソッドを「住所」と「クロージャ」の引数で呼び出している。
先ほどと同じように、「クロージャ」には変換結果とエラー情報の2つの変数が渡ってくるので、エラー情報に何も設定されていないときは変換成功としてサブタイトルに住所を設定し、ピンを刺す。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
// // ViewController.swift // import UIKit import MapKit class ViewController: UIViewController,MKMapViewDelegate{ @IBOutlet weak var testMapView: MKMapView! @IBOutlet weak var testTextField: UITextField! //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() //デリゲート先に自分を設定する。 testMapView.delegate = self //中心座標 let center = CLLocationCoordinate2DMake(35.690553, 139.699579) //表示範囲 let span = MKCoordinateSpanMake(0.001, 0.001) //中心座標と表示範囲をマップに登録する。 let region = MKCoordinateRegionMake(center, span) self.testMapView.setRegion(region, animated:true) } //長押し時の呼び出しメソッド @IBAction func pressMap(sender: UILongPressGestureRecognizer) { //マップビュー内のタップした位置を取得する。 let location:CGPoint = sender.locationInView(testMapView) //長押し終了 if (sender.state == UIGestureRecognizerState.Ended){ //タップした位置を緯度、経度の座標に変換する。 let mapPoint:CLLocationCoordinate2D = testMapView.convertPoint(location, toCoordinateFromView: testMapView) let location = CLLocation(latitude:mapPoint.latitude, longitude: mapPoint.longitude) //座標を住所に変換する。 let myGeocoder:CLGeocoder = CLGeocoder() myGeocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error) in if(error == nil) { for placemark in placemarks! { //ピンを地図に刺す。 let annotation = MKPointAnnotation() annotation.coordinate = CLLocationCoordinate2DMake(mapPoint.latitude, mapPoint.longitude) annotation.title = "住所" annotation.subtitle = "\(placemark.administrativeArea!)\(placemark.locality!)\(placemark.thoroughfare!)\(placemark.subThoroughfare!)" self.testMapView.addAnnotation(annotation) } } else { self.testTextField.text = "住所不明" } }) } } } |
以下は実際のプレイ動画
日本の住所で使われる、変換結果のプロパティの内容と例を下表にまとめる。
プロパティ名 | 内容 | 設定例 | |
---|---|---|---|
1 | postalCode | 郵便番号 | 160-0023 |
2 | country | 国 | 日本 |
3 | administrativeArea | 都道府県 | 東京都 |
4 | locality | 市区町村 | 新宿区 |
5 | subLocality | 西新宿 | |
6 | thoroughfare | 西新宿1丁目 | |
7 | subThoroughfare | 番地 | 7番3号 |