前説
iOS17から、MapKitがSwiftUIで定義することができます。
単純にマップを表示する場合は、以下のコードで表示できます。
import SwiftUI
import MapKit
struct ContentView: View {
// MARK: - State
// MARK: - Properties
// MARK: - View
var body: some View {
VStack {
Map()
}
}
}
地図を表示するだけならこれでいいのですが、 公式ドキュメント を見ていると scope
を初期化時に指定することができます。この scope
は、Namespaece.IDを指定して、デフォルトは nil
です。
なんとなくMapに名前空間を与えている印象はありますが、この scope
はどのように活用できるのでしょうか?
MapContorol における scope の活用
MapContorolは、例えば、ユーザーの現在地に地図を移動するボタンや、地図が向いている方向を示すコンパスアイコンのことです。これらを 地図とは違う場所に表示 したい場合に scope
を活用します。
以下は、サンプルコードです。
struct ContentView: View {
// MARK: - State
@Namespace var namespace
// MARK: - Properties
// MARK: - View
var body: some View {
VStack {
Map(scope: namespace)
HStack {
// コンパス
MapCompass(scope: namespace)
// ピッチトグル
MapPitchToggle(scope: namespace)
// 地図のスケール表示
MapScaleView(scope: namespace)
// 現在位置に移動させるボタン
MapUserLocationButton(scope: namespace)
}
}
.mapScope(namespace) // これを忘れずに記載
}
}
Stateとして @Namespace
を宣言しておきます。そして、Mapを初期化する際に namespace
を scope
の引数として渡します。
VStack の中に、コンパス、ピッチトグル、地図のスケール(縮尺)表示、現在地に移動させるボタンを HStack で宣言します。これらは、ぞれぞれ地図の外に表示されることになります(後述のデモを参照)。また、それぞれの構造体に、先程宣言した namespace
を scope
の引数として渡します。
そして、VStackに、mapScopeモディファイアを宣言して namespace
を渡します。
これをすることで、地図の動きに合わせて 地図外にコンパスを表示 したり、 地図外にある現在地ボタンを押してユーザーの現在位置に地図を移動 したりすることができます。
デモ
実際に動かすと以下のようになります(現在地ボタンは、アプリ側で位置情報の許可が有効になっていないと動作しません)。
補足
純粋に地図の上に位置情報ボタンなどを表示したい場合は、 scope
を使用せず以下のように宣言します。
import SwiftUI
import MapKit
struct ContentView: View {
// MARK: - State
// MARK: - Properties
// MARK: - View
var body: some View {
VStack {
Map()
.mapControls {
MapUserLocationButton()
}
}
}
}