Edited at

Swiftで位置検索ができるMapアプリの開発


何をやるのか?

今回はSwiftでの簡易的なMapアプリの開発を説明していく。

geocoderを用いた、緯度・経度の情報指定により検索ワードからその位置を割り出していく。


前提

xcodeのstoryboardにてMapKit、textfieldがすでに設置済みとして、プログラミングにおけるロジックにのみ焦点を当てて解説していく。


TextFieldの入力時の完了通知を受け取る

import UIKit

import MapKit

class ViewController: UIViewController , UITextFieldDelegate {

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
inputText.delegate = self
}

まずtextField のdelegate機能を使うために、ViewControllerに対してUITextFieldDelegateを宣言します。

そしてinputText.delegate = self を追記することで、初期画面表示前にTextField のdelegate通知先を設定します。

上記のdelegate通知先設定のコードを追記したviewDidLoad()メソッドは、初めて画面が表示される時に実行されるメソッドであるため、このメソッド内に追記します。


文字入力後の処理

@IBOutlet weak var inputText: UITextField!

<!-- ↑storybordからのGUI操作で宣言。TextField(検索窓)を宣言(表して)している。 -->
@IBOutlet weak var Map: MKMapView!
<!-- ↑storybordからのGUI操作で宣言。MapKit(地図)を宣言(表して)している。 -->

func textFieldShouldReturn(_ textField: UITextField) -> Bool {

//キーボードを閉じる。resignFirstResponderはdelegateメソッド
  textField.resignFirstResponder()

//入力された文字を取り出す
if let searchKey = textField.text {

//入力された文字をデバッグエリアの表示
print(searchKey)
}
  //デフォルト動作を行うのでtureを返す。返り値型をBoolにしているため、この記述がないとエラーになる。
return true
}

ここまでで検索窓から入力された値を取ってくる作業で終了です。


 キーワードから緯度経度を検索する

print(searchKey)

//CLGeocoderインスタンスを取得
let geocoder = CLGeocoder()

//入力された文字から位置情報を取得
geocoder.geocodeAddressString(searchKey, completionHandler: { (placemarks, error) in

//位置情報が存在する場合(定数geocoderに値が入ってる場合)はunwrapPlacemarksに取り出す。
if let unwrapPlacemark = placemarks {

//1件目の情報を取り出す
if let firstPlacemark = unwrapPlacemarks.first {

//位置情報を取り出す
if let location = firstPlacemark.location {

//位置情報から緯度経度をtargetCoordinateに取り出す
let targetCoordinate = location.coordinate
//緯度経度をデバッグエリアに表示
print(targetCoordinate)
}
}
}
})
}
return true
}
}

まず最初にCLGeocoderインスタンスを生成します。

CLGeocoderクラスを使うと、緯度軽度から住所を検索することができます。また、住所から緯度軽度を検索することだできます。


//CLGeocoderインスタンスを取得
let geocoder = CLGeocoder(

住所などの文字列から位置情報を取得するまでのメソッドです。

これは「クロージャ]という機能を使っています。クロージャとはここでは詳しく説明しませんが、関数とよく似た機能だと思ってください。

位置情報はクロージャ内に結果が通知され、位置情報が検索できたタイミングで{}内が実行されます。

geocoder.geocodeAddressString(searchKey, completionHandler: { (placemarks, error) in

}

通知された位置情報「placemarks」は現在の状態ではnillを含んでいる可能性がある、オプショナル型です。

そのためここからオプショナルバインディングを用いてアンラップすると同時に、情報を精査しという作業を繰り返します。

最終的にはtargetCoordinateにその地名の緯度経度の情報を格納します。

if let unwrapPlacemark = placemarks {

//1件目の情報を取り出す
if let firstPlacemark = unwrapPlacemarks.first {

//位置情報を取り出す
if let location = firstPlacemark.location {

//位置情報から緯度経度をtargetCoordinateに取り出す
let targetCoordinate = location.coordinate
//緯度経度をデバッグエリアに表示
print(targetCoordinate)


マップ上にピンを置く

//MKPointAnnotationインスタンスを取得し、ピンを生成

let pin = MKPointAnnotation()

//ピンの置く場所に緯度経度を設定
pin.coordinate = targetCoordinate

//ピンのタイトルを設定
pin.title = searchKey

//ピンを地図に置く
self.Map.addAnnotation(pin)

//検索地点の緯度経度を中心に半径500mの範囲を表示
self.Map.region = MKCoordinateRegion(center: targetCoordinate, latitudinalMeters: 500.0, longitudinalMeters: 500.0)

上記の通り、ピンの生成 → ピンに緯度経度を設定・タイトルを設定 → ピンを地図におく → 地図を拡大 という流れで検索地点を表示します。


最後にコード全体

import UIKit

import MapKit

class ViewController: UIViewController , UITextFieldDelegate {

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
inputText.delegate = self
}

@IBOutlet weak var inputText: UITextField!
@IBOutlet weak var Map: MKMapView!

func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()

if let searchKey = textField.text {

print(searchKey)

let geocoder = CLGeocoder()

geocoder.geocodeAddressString(searchKey, completionHandler: { (placemarks, error) in

if let unwrapPlacemarks = placemarks {
if let firstPlacemark = unwrapPlacemarks.first {
if let location = firstPlacemark.location {
let targetCoordinate = location.coordinate
print(targetCoordinate)

let pin = MKPointAnnotation()

pin.coordinate = targetCoordinate
pin.title = searchKey
self.Map.addAnnotation(pin)

self.Map.region = MKCoordinateRegion(center: targetCoordinate, latitudinalMeters: 500.0, longitudinalMeters: 500.0)
}
}
}
})
}
return true
}

}