はじめに
前から位置情報やマップを表示するアプリを作ってみたいと思ってたので作ってみた。
動作環境
- Xcode 10.1
- Swift 4.2.1
位置情報を使用する上で知っておくこと
位置情報に関する要素
- Core Location(デバイスの地理的位置と向きを取得する)
地図表示に関する要素
- MapKit(地図を表示する)
認証リクエスト
- iOSアプリでは、ユーザーの位置や連絡先などプライバシーに関わる情報を取得する前にユーザーから許可を得る必要がある。(認証リクエストAPIを呼び出す必要がある)
- 認証リクエストAPIには使用用途に応じて次の2種類がある
- 起動時のみ:
requestWhenInUseAuthorization
- 常時:
requestAlwaysAuthorization
- 起動時のみ:
- requestWhenInUseAuthorization アプリまたはその機能が画面上に表示されている時だけ、位置情報の利用を許可するもので、アプリがバックグラウンド状態の時は、許可されない。
- requestAlwaysAuthorization バックグラウンドで実行中の場合にも位置情報の利用を許可するものです。
この認証APIは、何度でも呼び出すことができますが、ダイアログが表示されるのはステータスが NotDetermined(未設定) の時だけです。 したがって、一度ダイアログが表示されてユーザが「許可する/しない」の選択をした後は、このメソッドを呼び出しても何も起こりません。
認証の種類
- NotDetermined: まだ設定していない
- restricted: 「設定」⇒「一般」⇒「機能制限」で、位置情報サービスの利用が制限されている
- denied: 「許可しない」に設定されている
- authorizedAlways: 「常に許可」に設定されている。
- authorizedWhenInUse: 「このAppの使用中のみ許可」に設定されている。
利用目的の記載
認証リクエストを呼び出すためには、「位置情報を利用する目的」を Info.plist
に記載する必要がある。
requestWhenInUseAuthorization → Privacy - Location When In Use Usage Description
requestAlwaysAuthorization → Privacy - Location Always and When In Use Usage Description
ダイアログでユーザが選択した内容は、アプリの設定情報として記憶され、これをアプリ側から変更することは出来ない。
マップをViewに表示する
手順
- プロジェクト新規作成
- フレームワーク導入
- ViewControllerにMapKitとCoreLocationをimportし、main.storyboard にMapViewを配置する
- 実行
プロジェクト作成からビルドしてシミュレーターでマップを表示させるところまでをまずはやっていく
1. プロジェクト新規作成
XcodeのCreate a New Xcode Project から Single View Applicationを選択します。
Product Nameは MapApp (任意)
Languageは Swift
Devicesは iPhone
をそれぞれ選択します。
2. フレーワーク導入
TARGETS > Build Phases > Link Binary With Libraries から MapKit.framework, Core Location.framework を追加します
3. ViewControllerにMapKitとCoreLocationをimportし、main.storyboard にMapViewを配置する
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController {
@IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
}
4. 実行
これで実行すればこんな感じ!!!
認証をリクエストし、自分の位置情報と目的地のピンをMap上に表示する
import UIKit
import MapKit
import CoreLocation
class ViewController: UIViewController {
@IBOutlet weak var mapView: MKMapView!
var locationManager: CLLocationManager!
fileprivate func initializeLocationManager() {
locationManager = CLLocationManager()
}
fileprivate func requestAuthorization() {
locationManager.requestAlwaysAuthorization()
}
fileprivate func setupLocationManagerIfNeeded() {
let status = CLLocationManager.authorizationStatus()
if status == .authorizedWhenInUse || status == .authorizedAlways {
locationManager.delegate = self
locationManager.distanceFilter = 10
locationManager.startUpdatingLocation()
}
}
override func viewDidLoad() {
super.viewDidLoad()
initializeLocationManager()
requestAuthorization()
setupLocationManagerIfNeeded()
}
}
extension ViewController: CLLocationManagerDelegate {
// 位置情報取得に失敗したときに呼び出される
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print(error.localizedDescription)
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
// 取得した位置情報の緯度経度
let location = locations.first
let latitude = location?.coordinate.latitude ?? 0
let longitude = location?.coordinate.longitude ?? 0
let locationCoordinate = CLLocationCoordinate2DMake(latitude, longitude)
// 表示するマップの中心を、取得した位置情報のポイントに指定
mapView.setCenter(locationCoordinate, animated: true)
var region = mapView.region
region.center = locationCoordinate
region.span.latitudeDelta = 0.02
region.span.longitudeDelta = 0.02
mapView.setRegion(region, animated: true)
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(35.696135, 139.768322)
annotation.title = "目的地"
self.mapView.addAnnotation(annotation)
// 位置情報取得を停止
locationManager.stopUpdatingLocation()
}
}