🌏

SwiftUIのMapKitで現在位置を青く点滅させる

2024/04/14に公開

🤔やってみたいこと

  1. SwiftUI MapKitを使って現在位置を取得
  2. 現在位置を青く点滅させる
  3. よく見かける地図アプリぽくしたい

過去の動画を参考にしたが、どうやらうまいくいかないようだ?

🚀やってみたこと

位置情報サービスを使用するためには、Info.plistファイルに特定のキーと説明文を追加する必要があります。

具体的には以下のステップを行います:

🔧Xcode で対象のプロジェクトを開きます

プロジェクトナビゲーターで、プロジェクトの「Info.plist」ファイルを探し、選択します
「Information Property List」セクションで、右上の「+」ボタンをクリックし、新しいキーを追加します
表示されるウィンドウで「Privacy - Location When In Use Usage Description」キーを選択します
キーの横にある値の欄に、ユーザに向けた位置情報使用の理由を説明する文字列を入力します (例: "このアプリは、あなたの現在地を地図上に表示するために位置情報を使用します")
この設定を追加すると、最初にアプリを実行したときに、位置情報サービスの使用許可を求めるアラートがポップアップ表示されます。ユーザーが許可を選択すれば、位置情報へのアクセスが可能になります。

補足ですが、常に位置情報にアクセスしたい場合は、「Privacy - Location Always and When In Use Usage Description」キーも追加する必要があります。

Info.plistへのこの設定は、位置情報サービスを適切に使用するためのApple の要件となっています。設定を怠ると、デバイスがあなたのアプリに位置情報へのアクセスを許可しません。

  1. MapビューにshowsUserLocation: trueを追加。これにより自分の位置が青い円でマップ上に表示されます。
  2. regionプロパティの初期値を東京駅の座標に設定しています。位置情報が取得できれば自動的に現在地の座標に更新されます。
  3. locationManager(_:didUpdateLocations:)内で、取得した位置情報から新しいregionを生成し、プロパティに代入するようにしています。

この変更で、ユーザーの位置が青い円で表示されるはずです。また、地図の中心も自動的にその位置に移動します。ピンのアノテーションと併せて、現在地がマップ上で確認できるようになります。

全体のソースコード:

import SwiftUI
import MapKit
import CoreLocation
import Combine

// ContentViewはSwiftUIのViewを表すStructです。
struct ContentView: View {
    // LocationManagerクラスのインスタンスを@StateObjectプロパティとして持っています。
    @StateObject private var locationManager = LocationManager()

    var body: some View {
        // Mapビューを表示します。
        // coordinateRegionプロパティにlocationManager.regionをバインディングしています。
        // showsUserLocationをtrueにすると、ユーザーの現在位置が青い円で表示されます。
        // annotationItemsにlocationManager.annotationsを指定し、MapMarkerを使ってアノテーションを表示します。
        Map(coordinateRegion: $locationManager.region, showsUserLocation: true, annotationItems: locationManager.annotations) { annotation in
            MapMarker(coordinate: annotation.coordinate)
        }
        .edgesIgnoringSafeArea(.all) // Mapビューをエッジまで広げて表示します。
        .onAppear {
            // ContentViewが表示されたときに、locationManager.requestLocation()を呼び出します。
            locationManager.requestLocation()
        }
    }
}

// LocationManagerクラスはCLLocationManagerDelegateに準拠し、位置情報の取得と管理を行います。
class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    // CLLocationManagerのインスタンスを持っています。
    private let manager = CLLocationManager()

    // 位置情報のアノテーションを保持する配列です。
    @Published private(set) var annotations: [LocationAnnotation] = []

    // 地図の表示領域を表すMKCoordinateRegionのPublishedプロパティです。
    @Published var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 35.6895, longitude: 139.6917), span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))

    override init() {
        super.init()
        // CLLocationManagerのdelegateをselfに設定します。
        manager.delegate = self
        // 高精度の位置情報を要求します。
        manager.desiredAccuracy = kCLLocationAccuracyBest
    }

    // 位置情報の取得を開始します。
    func requestLocation() {
        // ユーザーに位置情報の使用許可を求めます。
        manager.requestWhenInUseAuthorization()
    }

    // 位置情報が更新されたときに呼ばれるdelegateメソッドです。
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        // 最新の位置情報を取得します。
        guard let location = locations.last else { return }

        // 位置情報からLocationAnnotationのインスタンスを作成します。
        let annotation = LocationAnnotation(coordinate: location.coordinate)
        // annotationsプロパティに新しいアノテーションを設定します。
        annotations = [annotation]

        // 新しい位置情報から地図の中心座標を計算します。
        let center = CLLocationCoordinate2D(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude)
        let span = MKCoordinateSpan(latitudeDelta: 0.01, longitudeDelta: 0.01) // 適切なスパン(ズームレベル)を設定します。
        region = MKCoordinateRegion(center: center, span: span) // 新しいregionを設定します。

        // メインスレッドでobjectWillChange.send()を呼び出します。
        // これにより、PublishedプロパティのObserverに変更が通知されます。
        DispatchQueue.main.async {
            self.objectWillChange.send()
        }
    }
}

// LocationAnnotationはIdnetifiableプロトコルに準拠する構造体で、地図上のアノテーションを表します。
struct LocationAnnotation: Identifiable {
    let id = UUID() // アノテーションのユニークなIDです。
    let coordinate: CLLocationCoordinate2D // アノテーションの座標です。
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

AppleDeveloperのアカウントを持っている人は、実機でビルドできると思うので、お持ちのiPhoneで試してみたください。こんな感じで使えます。



🙂最後に

今回は、SwiftUIでMapKitを使って東京都をデフォルトで表示する位置に設定して、表示されているところは渋谷近辺なんですけど、私がいるのが三鷹市だったので、その辺まで画面を移動すると、青く点滅している丸がありましたので、今回作りたい機能を実現することができました!

Jboy王国メディア

Discussion