例えばお店の位置を知らせる地図は緯度経度からすぐにできますが、自分が今どこにいるのか知りたいという場合には、ユーザー許可を取得したりして少し手間がかかります。
Xcode 11.3.1
requestWhenInUseAuthorization
ある決まった場所を地図で紹介するときは緯度経度が確定しているので簡単にできました。
では、ユーザーが今いるところが知りたいケースではどうするのでしょうか。
ユーザー許可のリクエスト
位置情報は(少なくとも日本では)個人情報保護の観点から、ユーザー許可を取る必要がでてきます。
NSLocationWhenInUseUsageDescription をKeyとして、説明をInfo.plistに書き込みます。これは位置情報を取得するときのユーザーへの説明です。こっそり裏で位置情報を取ることはできません(審査で落ちるでしょう)
Info.plist
1 2 3 4 5 6 7 8 9 |
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>NSLocationWhenInUseUsageDescription</key> <string>位置情報を使います。</string> ... </dict> </plist> |
これを呼び出すためのコードは
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// CLLocationManagerのインスタンス生成 var locationManager = CLLocationManager() //位置情報サービスの確認 CLLocationManager.locationServicesEnabled() // セキュリティ認証のステータス let status = CLLocationManager.authorizationStatus() if(status == CLAuthorizationStatus.notDetermined) { // 許可をリクエスト locationManager.requestWhenInUseAuthorization() } |
notDeterminedのステータスが返ってきたら
requestWhenInUseAuthorization()
を投げて許可を求めるダイアログを表示させます。
カレンダーアプリなのに位置情報を取得しようとするとユーザーが疑問を持ちますので、アプリのこういった機能のためであるなど、理由を説明することができます。
userLocation.coordinate
固定の緯度経度を設定したCLLocationCoordinate2Dの代わりに、現在位置はこれを使って取得できます。
また、ユーザーが移動して位置が変わった時も追従するように設定。
startUpdatingLocation()
常に追従させていく方法として、トラッキングモードも設定できます。
MKUserTrackingMode.follow
MKUserTrackingMode.followWithHeading
NativeのMapsアプリではSafeAreaを考慮して実装してありました(あたりまえ!)
それを参考に、Portraitではtopとbottomには地図を表示させないようにしました。
ちなみに、Mapsではtopは薄く透明にしてbottomはSearchバーを置いています。
サンプルコード
位置情報は今では個人情報ですが以前はなあなあだった時もあります。
Simulatorでテストするとエラーになります。
1 2 3 |
... ibMobileGestalt MobileGestalt.c:1647: Could not retrieve region info ... |
これは実際の位置情報が重要だということでしょう、実機で試せば大丈夫です。
ViewController.swift
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 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
import UIKit import MapKit import CoreLocation class ViewController: UIViewController ,MKMapViewDelegate, CLLocationManagerDelegate{ var locationManager = CLLocationManager() override func viewDidLoad() { super.viewDidLoad() locationManager.delegate = self // 画面背景色を設定 self.view.backgroundColor = UIColor(red:0.7,green:0.7,blue:0.7,alpha:1.0) } // 画面回転にも対応する override func viewDidAppear(_ animated: Bool) { var topPadding: CGFloat = 0 var bottomPadding: CGFloat = 0 var leftPadding: CGFloat = 0 var rightPadding: CGFloat = 0 if #available(iOS 11.0, *) { // 'keyWindow' was deprecated in iOS 13.0: Should not be used for applications let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first topPadding = window!.safeAreaInsets.top bottomPadding = window!.safeAreaInsets.bottom leftPadding = window!.safeAreaInsets.left rightPadding = window!.safeAreaInsets.right } //位置情報サービスの確認 CLLocationManager.locationServicesEnabled() // セキュリティ認証のステータス let status = CLLocationManager.authorizationStatus() if(status == CLAuthorizationStatus.notDetermined) { print("NotDetermined") // 許可をリクエスト locationManager.requestWhenInUseAuthorization() } else if(status == CLAuthorizationStatus.restricted){ print("Restricted") } else if(status == CLAuthorizationStatus.authorizedWhenInUse){ print("authorizedWhenInUse") } else if(status == CLAuthorizationStatus.authorizedAlways){ print("authorizedAlways") } else{ print("not allowed") } // 位置情報の更新 locationManager.startUpdatingLocation() // MapViewのインスタンス生成. let mapView = MKMapView() // MapViewをSafeAreaに収める(Portraitのケース) // 以降、Landscape のみを想定 let screenWidth = view.frame.size.width let screenHeight = view.frame.size.height let rect = CGRect(x: leftPadding, y: topPadding, width: screenWidth - leftPadding - rightPadding, height: screenHeight - topPadding - bottomPadding ) mapView.frame = rect // Delegateを設定. mapView.delegate = self // 縮尺を設定 var region:MKCoordinateRegion = mapView.region region.center = mapView.userLocation.coordinate region.span.latitudeDelta = 0.02 region.span.longitudeDelta = 0.02 mapView.setRegion(region,animated:true) // MapViewをViewに追加. self.view.addSubview(mapView) mapView.mapType = MKMapType.hybrid mapView.userTrackingMode = MKUserTrackingMode.follow mapView.userTrackingMode = MKUserTrackingMode.followWithHeading } func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { print("region changed") } } |
References:
MKMapView – MapKit | Apple Developer Documentation
MKUserTrackingMode – MapKit | Apple Developer Documentation