【SwiftUI】現在地を取得して表示する方法!MapKitで地図アプリ

この記事からわかること
- Swift UIで地図(Maps)を表示する方法
- MapKitフレームワークの使い方
- ユーザーの現在地を常に取得する方法
- 現在地を常に取得しMapの表示領域を更新し続ける方法
- ボタンをクリックごとに表示領域を更新し続ける方法方法
- CLLocationManagerの使い方
- locationManagerメソッド(デリゲートメソッド)とは?
- 位置情報取得の許可/拒否で処理を切り替える
index
[open]
- CoreLocationとMapKitフレームワーク
- 1.SwiftUIで地図の表示方法をおさらい
- 2.現在地を常に取得しMapの表示領域を更新し続ける方法
- プロジェクトのinfoパネルからユーザー利用許可を追加する
- 位置情報を管理するクラスの作成
- CLLocationManagerクラスの設定
- 領域を更新するデリゲートメソッド
- locationManagerメソッドとは?
- ユーザートラッキングモードを追従モードにして表示
- 3.ボタンのクリックごとに位置情報を取得し更新する
- シミュレーターで現在地の更新を試す方法
- 4.位置情報取得の許可/拒否で処理を切り替える
- locationManagerDidChangeAuthorizationメソッド
- 列挙型CLAuthorizationStatus
\ アプリをリリースしました /
環境
- Xcode:16.0
- iOS:18.0
- Swift:5.9
- macOS:Sonoma 14.6.1
今回はSwiftUIでMapKitフレームワークを使った現在地の取得方法をまとめていきます。MapKitの使い方や地図の表示方法については別記事で解説していますので参考にしてください。
【SwiftUI】地図(Maps)を表示するMapKitの使い方!Map()とは?
CoreLocationとMapKitフレームワーク
実装していく機能
- 地図を表示
- 現在地を常に取得しMapの表示領域を更新し続ける方法
- ボタンのクリックごとに位置情報を取得し更新する
- 位置情報取得の許可/拒否で処理を切り替える
SwiftUIで現在地を取得するにはCoreLocationフレームワーク
のCLLocationManager
クラスを使用します。地図を表示、操作できるMapKitフレームワーク
にはCoreLocationフレームワーク
が組み込まれているのでMapKitをインポートするだけで地図の表示から現在地の取得、さらにはジオコーディング(住所→座標への変換)などが簡単に行えるようになります。
1.SwiftUIで地図の表示方法をおさらい
一番初めにMapKitフレームワークを使用して地図を表示する際のコードとポイントをおさらいしておきます。

ここでは詳しい説明は省きますが、以下のポイントは理解しておいてください。
地図を表示させるポイント
- MKCoordinateRegion構造体と各プロパティ
- 座標/緯度/経度
- MapView
2.現在地を常に取得しMapの表示領域を更新し続ける方法
今回はSwiftUIを使用した場合の「現在地を常に取得しMapの表示領域を更新し続ける方法」をまとめていきます。
実装していく流れ
- プロジェクトのinfoパネルからユーザー利用許可を追加する
- 位置情報を管理するクラスの生成
- デリゲートメソッドの実装
プロジェクトのinfoパネルからユーザー利用許可を追加する
アプリ内でユーザーの現在地を取得して使用する場合はユーザーの許可が必要になります。アプリ起動時にポップアップで確認事項が出るようにinfoパネルから設定を追加しておきます。
プロジェクトを開いたら左のナビゲータエリアから「プロジェクト名」をクリック、「info」タブに切り替えます。
その中の「Custom iOS Target Properties」の中に新たな項目を追加します。どこでも良いのですが「Bundle Category」の横にある「」をクリックすると以下のように新たな「Key」が追加できるようになるので「Privacy - Location When In Use Usage Description」を選択します。

右側の「Value」にはポップアップで表示したい確認文を記述しておいてください。追加するとアプリ起動時に以下のように許可を求めるポップアップが表示されるようになります。

位置情報を管理するクラスの作成
続いて新規のファイルを作成し、位置情報を管理するクラスを作成していきます。「File」>「new」>「File...」から「Cocoa Touch Class」を選択し「MapModels.swift」ファイルを作成しておきます。

既存のクラスを書き換えLocationManager
クラスとしておきます。スーパクラスはNSObject
、プロトコルはObservableObject
、CLLocationManagerDelegate
の2つに準拠させておきます。
各スーパクラス/プロトコルの役割
- NSObject:イニシャライザをオーバーライド
- ObservableObject:クラスの変更を観測
- CLLocationManagerDelegate:locationManager(_:didFailWithError:)を実装
CLLocationManagerクラスの設定
まずは現在地を取得するための準備をしていきます。クラス内でCLLocationManager
クラスをインスタンス化し、スーパクラスのイニシャライザをオーバーライド(元のクラスのイニシャライザは変更せず引き継ぐ)して処理を追加していきます。
イニシャライザ内ではインスタンス化したCLLocationManager
クラスのプロパティやメソッドを呼び出して値を設定していきます。このクラスを操作することで位置情報に関するイベントを操作することが可能になります。
delegateプロパティ
delegate
プロパティにはCLLocationManagerDelegateプロトコルに準拠したクラスを指定しなければなりません。今回の場合はデリゲートの「処理を任せるクラス」と「処理を任されるクラス」が同じなので自分自身(クラス)を指すselfを格納します。
requestWhenInUseAuthorizationメソッド
利用許可のアラートを表示させるメソッドです。WhenInUseAuthorization
の言葉通り「使用中のみの許可」を申請していますがポップアップには「Appの使用中は許可」と「1度だけ許可」の2つが表示されます。表示させるアラートに関するメソッドは以下の通りです。
requestAlwaysAuthorization
を使用する場合は「infoパネルからユーザー利用許可を追加」で「Privacy Location Always Usage Description」項目を追加してください。
desiredAccuracyプロパティ
位置情報の正確さを設定できるプロパティです。
精度が高い位置情報を要求する場合はデバイスのバッテリー消費も嵩むので適切な値を指定することが重要になります。
distanceFilterプロパティ
位置情報の更新頻度(メートル単位)を数値で指定するプロパティです。
定義を見ると設定値の型はCLLocationDistance
となっていますがこれはDouble
型のタイプエイリアスです。
startUpdatingLocationメソッド
ユーザーの現在地を監視し追跡をスタートさせるメソッドです。
位置情報が更新されるとlocationManager(_:didUpdateLocations:)
メソッドが呼び出されデリゲートへと情報が譲渡されていきます。
領域を更新するデリゲートメソッド
続いてデリゲートメソッド内に領域を更新する処理を実装していきます。先ほどstartUpdatingLocationメソッド
から呼び出されるlocationManager(_:didUpdateLocations:)
がデリゲートメソッドになります。
locationManagerメソッドとは?
locationManager
メソッドはCLLocationManagerDelegate
プロトコルにあらかじめ定義されているメソッド(デリゲートメソッド)です。CLLocationManagerDelegate
定義を見ると「定義を必須としないため」のoptional
キーワードがついているので未定義でもエラーは発生しません。
定義を見ると数多くの同名のメソッドが定義されています。これらの違いは呼び出されるタイミングと引数で受け取れる値の違いです。
種類 | 概要 | 引数 |
---|---|---|
locationManager (_:didUpdateLocations locations: [CLLocation]) |
位置情報が更新されたタイミング | 位置データを含むオブジェクトの配列 [CLLocation] |
locationManagerDidChangeAuthorization(_:) | 位置情報と認証状態が更新されたタイミング | - |
locationManager(_:didUpdateHeading newHeading: CLHeading) | 進行方向が更新されたタイミング | デバイスの方位角(方向) |
locationManagerメソッドの役割
- デリゲートメソッド
- CLLocationManagerDelegateプロトコルに定義
- 同名のメソッドが複数ある
- optionalが指定されており定義は必須でない
- 今回は位置情報が更新されるたびに実行される
- 明示的に呼び出しはせずstartUpdatingLocationメソッドが自動で呼び出す
ユーザートラッキングモードを追従モードにして表示
最後に表示部分を調整していきます。LocationManager
クラスの領域情報を保持するregion
プロパティは@Published
で更新を観測しているので適応されるように@ObservedObject
をつけた変数にインスタンス化します。
MapView
の引数に領域を保持するregion
とマップ上にユーザーの場所を表示させるための
Bool値
、位置情報の更新に対してのトラッキングモードを指定します。
これで「現在地を常に取得しMapの表示領域を更新し続ける機能」の実装が完了しました。
シミュレーション方法に関しては後述しています。
「シミュレーターで現在地の更新を試す方法」へジャンプする
3.ボタンのクリックごとに位置情報を取得し更新する
続いて「ボタンのクリックごとに位置情報を取得し更新する」方法をみていきたいと思います。
ユーザーの位置情報はCLLocationManager
クラスのlocation
プロパティに格納されています。位置情報の取得が許可されていない場合はnil
が格納されます。
新しく定義したreloadRegion
メソッドの中にオプショナルバインディングを使用して変数に格納します。位置情報はCLLocation
型なので緯度や経度にアクセスすることができるので同様の手順でregion
を作成していきます。
あとはこのメソッドを適当なボタンのクリック時に実行されるようにすれば「ボタンのクリックごとに位置情報を取得し更新する」機能の実装は完了です。
シミュレーターで現在地の更新を試す方法
アプリ内で現在位置を使用する場合プレビューや簡易的なライブプレビューでは現在地の更新とともに地図が変更する様子は確認できません。
動作確認をするにはツールバーのRunボタン( )をクリックしてシミュレーターを起動します。
シミュレーターが起動したら上のメニューから「Features」>「Location」>「Freeway Drive」をクリックするとシミュレーターのユーザーの位置表示が動き出し、正常に動作していることが確認できます。

シミュレーターは「SE」や「iPhone 11」などでは問題なく動作しましたが、「iPhone 13」では以下のようなエラーが発生し起動できなかったので注意してください。
4.位置情報取得の許可/拒否で処理を切り替える
ユーザーが位置情報の取得の承認申請に対して「許可または拒否」した場合に処理を分岐させる方法をみていきます。
悩み
位置情報を拒否されるとマップが機能しなくなる(マップの初期表示位置に現在地を渡すため)
解決方法
拒否された場合は仮の初期表示位置を渡す
今回は位置情報(現在地)の取得が拒否された時にマップ機能が使えるようにデフォルトの位置情報を渡すようにしたいと思います。
locationManagerDidChangeAuthorizationメソッド
実装するには位置情報の認証状態が変更された時に呼び出されるlocationManagerDidChangeAuthorization
メソッドを使用します。このメソッドは変更された時に実行されるだけなのでその中に認証状態により処理を分岐させる処理を記述する必要があります。
ユーザー選択した認証状態はauthorizationStatus
プロパティで取得することができます。このプロパティの値は列挙型CLAuthorizationStatus
に定義されている値になります。
列挙型CLAuthorizationStatus
列挙型CLAuthorizationStatus
には以下の値が定義されています。
以下のようにlocationManagerDidChangeAuthorization
メソッド内でauthorizationStatus
プロパティを参照し列挙型の値によって処理を分岐させることで解決できました。
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。
私がSwift UI学習に使用した参考書