iOS 標準の地図ライブラリ MapKit と位置情報取得クラス CLLocationManager を利用して地図アプリの土台になりそうなものを作ってみた。
Swift によるサンプルコード。ほぼコピペで動作する (他には Info.plist の編集が必要)。Storyboard は使わない。
今回の動作確認環境: Xcode 7.2 + Swift 2.1.1 + 実機 iPhone 6 + iOS 9.2
サンプルコードを動かすためにやること概要
- Xcode で新規に Single View Application のプロジェクトを作成
- ViewController.swift にサンプルコードをコピペ
- Info.plist に NSLocationWhenInUseUsageDescription または NSLocationAlwaysUsageDescription を追加
プロジェクト作成と ViewController.swift
Xcode で新規に Single View Application のプロジェクトを作成。
プロジェクトに特別な設定は必要なし。
以下のコードを ViewController.swift に上書きで貼り付ける。
//
// ViewController.swift
//
import UIKit
import MapKit
class ViewController: UIViewController, UIToolbarDelegate, MKMapViewDelegate, CLLocationManagerDelegate {
// 地図
private var myMapView : MKMapView?
// ツールバー
private var myToolbar: UIToolbar?
private var myLocationButton: UIBarButtonItem?
// 位置情報
private var myLocationManager: CLLocationManager?
override func viewDidLoad() {
super.viewDidLoad()
// 地図
myMapView = ViewController.createMapView(self.view, delegate: self)
self.view.addSubview(myMapView!)
// ツールバー
(myToolbar, myLocationButton) =
ViewController.createToolbar(
self.view,
target: self,
locationButtonSelector: "onClickLocationButton:")
self.view.addSubview(myToolbar!)
// 位置情報
myLocationManager = ViewController.createLocationManager(self)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
private static func createMapView(parentView: UIView, delegate: MKMapViewDelegate) -> MKMapView {
// MKMapView の生成
let mapView = MKMapView()
// MapView のサイズを指定
mapView.frame = parentView.bounds
// MKMapView に Delegate を設定
mapView.delegate = delegate
// 中心点の緯度経度(名古屋駅)
let lat: CLLocationDegrees = 35.1707223
let lon: CLLocationDegrees = 136.882136
let coordinate: CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat, lon)
// 縮尺
let latDist : CLLocationDistance = 100
let lonDist : CLLocationDistance = 100
// Region を生成
let region: MKCoordinateRegion =
MKCoordinateRegionMakeWithDistance(coordinate, latDist, lonDist);
// MKMapView に反映
mapView.setRegion(region, animated: true)
return mapView
}
private static func createToolbar(parentView: UIView,
target: AnyObject, locationButtonSelector: Selector) -> (UIToolbar?, UIBarButtonItem?) {
// ツールバー
let toolbar =
UIToolbar(frame: CGRectMake(0, 0, parentView.bounds.size.width, 44.0))
toolbar.layer.position =
CGPoint(x: parentView.bounds.width / 2, y: parentView.bounds.height - 22.0)
// ツールバーの色
toolbar.barStyle = .BlackTranslucent
toolbar.tintColor = UIColor.whiteColor()
toolbar.backgroundColor = UIColor.blackColor()
// ボタンを生成
let locationButton =
UIBarButtonItem(
title: "現在地",
style:.Plain,
target: target,
action: locationButtonSelector)
// ボタンをツールバーに入れる
toolbar.items = [locationButton]
return (toolbar, locationButton)
}
private static func requestLocationAuthorization(lm: CLLocationManager) {
// アプリ使用中のみ位置情報を利用する場合 (「このApp使用中のみ許可」と設定情報に表示される)
// Info.plist にキーと説明文を追加する必要がある
// Key: NSLocationWhenInUseUsageDescription
// Type: String
// Value: 任意の説明文
lm.requestWhenInUseAuthorization()
// バックグラウンドでも位置情報を利用する場合 (「常に許可」と設定情報に表示される)
// Info.plist にキーと説明文を追加する必要がある
// Key: NSLocationAlwaysUsageDescription
// Type: String
// Value: 任意の説明文
//lm.requestAlwaysAuthorization()
}
private static func createLocationManager(delegate: CLLocationManagerDelegate) -> CLLocationManager {
// CLLocationManager に Delegate を設定
let locationManager = CLLocationManager()
locationManager.delegate = delegate
// 位置情報取得の認証状況を取得
let status = CLLocationManager.authorizationStatus()
switch status {
case .NotDetermined:
// 未認証状態
print("status: NotDetermined")
// 位置情報取得リクエストダイアログを表示する
requestLocationAuthorization(locationManager)
case .Restricted:
print("status: Restricted")
break
case .Denied:
print("status: Denied")
break
case .AuthorizedAlways:
// 認証済み
print("status: AuthorizedAlways")
break
case .AuthorizedWhenInUse:
// 認証済み
print("status: AuthorizedWhenInUse")
break
}
return locationManager
}
private static func startLocation(lm: CLLocationManager, button: UIBarButtonItem) {
button.title = "現在地"
button.style = .Done
// 位置情報取得開始
lm.startUpdatingLocation()
}
private static func stopLocation(lm: CLLocationManager, button: UIBarButtonItem) {
button.title = "現在地"
button.style = .Plain
// 位置情報取得停止
lm.stopUpdatingLocation()
}
func onClickLocationButton(sender: UIBarButtonItem) {
if sender.style == .Done{
ViewController.stopLocation(myLocationManager!, button: sender)
}else{
ViewController.startLocation(myLocationManager!, button: sender)
}
}
// 位置情報取得成功時に実行される関数
func locationManager(lm: CLLocationManager,
didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation) {
// 緯度経度を取得
let latitude = newLocation.coordinate.latitude
let longitude = newLocation.coordinate.longitude
print("latiitude: \(latitude) , longitude: \(longitude)")
let location:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
// 地図を移動
myMapView!.setCenterCoordinate(location, animated: false)
// 位置情報取得停止
ViewController.stopLocation(lm, button: myLocationButton!)
}
// 位置情報取得失敗時に実行される関数
func locationManager(lm: CLLocationManager, didFailWithError error: NSError) {
print("locationManager: :\(error)");
// 位置情報取得停止
ViewController.stopLocation(lm, button: myLocationButton!)
// 位置情報設定画面へのダイアログ表示
ViewController.showLocationConfigureDialog(self)
}
// 位置情報設定画面へのダイアログ表示
private static func showLocationConfigureDialog(view: UIViewController) {
let alert = UIAlertController(title: "位置情報が取得できません。",
message: "位置情報を利用する場合は設定をしてください。",
preferredStyle: .Alert)
// 選択肢を構築
alert.addAction(
UIAlertAction(
title: "位置情報の設定をする",
style: .Default,
handler: { action in
// 位置情報設定画面へ遷移する
let url = NSURL(string: UIApplicationOpenSettingsURLString)
UIApplication.sharedApplication().openURL(url!)
}))
alert.addAction(UIAlertAction(title: "キャンセル", style: .Cancel, handler: nil))
// ダイアログを表示する
dispatch_async(dispatch_get_main_queue(), {
view.presentViewController(alert, animated: true, completion: nil)
})
}
}
Info.plist (information property list file) の編集
位置情報を取得するためには Info.plist にキーと説明文を追加する必要がある。
Xcode の左側のファイル一覧から Info.plist を選択。
項目の追加は、右クリックして Add Row で可能。
Info.plist の編集: 「このApp使用中のみ許可」の場合
アプリ使用中のみ位置情報を利用する場合 (「このApp使用中のみ許可」と設定情報に表示される) に追加する内容。
Key: NSLocationWhenInUseUsageDescription
Type: String
Value: 任意の説明文
位置情報の取得許可をユーザーに得る際には、アプリの中で CLLocationManager.requestWhenInUseAuthorization() をコールして、ダイアログを表示する。
Info.plist の編集: 「常に許可」の場合
バックグラウンドでも位置情報を利用する場合 (「常に許可」と設定情報に表示される) に追加する内容。
Key: NSLocationAlwaysUsageDescription
Type: String
Value: 任意の説明文
位置情報の取得許可をユーザーに得る際には、アプリの中で CLLocationManager.requestAlwaysAuthorization() をコールして、ダイアログを表示する。
ユーザーが位置情報取得を許可しなかった場合の挙動
位置情報取得に失敗したときに呼ばれるメソッドがある。
func locationManager(lm: CLLocationManager, didFailWithError error: NSError)
サンプルコードでは、この関数の中で「位置情報が取得できません」と、ダイアログを表示している。
ユーザーがダイアログの「位置情報の設定をする」をタップすれば、設定アプリの該当箇所が開くようにした。
ユーザーは、設定画面から位置情報の利用を許可できる。
iOS の「設定」アプリ → [プライバシー] → [位置情報サービス] からも、設定状況を確認・変更できる。
参考資料
-
ref.
- About Information Property List Files
- Cocoa Keys
- NSLocationAlwaysUsageDescription
- NSLocationWhenInUseUsageDescription
- iOS 8 の位置情報のプライバシー設定に対応する – I'm Sei.
- iOS8で復活した設定画面へのURLスキーム - Qiita
tags: swift map
Posted by NI-Lab. (@nilab)