1. 始める前に
この Codelab では、Maps 3D SDK for iOS を使用して SwiftUI で 3D 地図アプリを作成する方法について説明します。
学習内容:
- カメラを操作して場所を表示したり、地図上を移動したりする方法。
- マーカーとモデルを追加する方法
- ラインとポリゴンを描画する方法
- ユーザーがプレイス マーカーをクリックしたときに処理する方法。
前提条件
- 課金が有効になっている Google コンソール プロジェクト
- API キー(必要に応じて Maps 3D SDK for iOS に制限可能)。
- SwiftUI を使用した iOS 開発に関する基本的な知識。
演習内容
- Xcode を設定し、Swift Package Manager を使用して SDK を導入する
- API キーを使用するようにアプリを設定する
- 基本的な 3D 地図をアプリに追加する
- カメラを操作して特定の場所を飛行する
- 地図にマーカー、ライン、ポリゴン、モデルを追加する
必要なもの
- Xcode 15 以降。
2. セットアップする
次の有効化の手順では、Maps 3D SDK for iOS を有効にする必要があります。
Google Maps Platform を設定する
課金を有効にした Google Cloud Platform アカウントとプロジェクトをまだ作成していない場合は、Google Maps Platform スタートガイドに沿って請求先アカウントとプロジェクトを作成してください。
- Cloud Console で、プロジェクトのプルダウン メニューをクリックし、この Codelab に使用するプロジェクトを選択します。
- Google Cloud Marketplace で、この Codelab に必要な Google Maps Platform API と SDK を有効にします。詳しい手順については、こちらの動画またはドキュメントをご覧ください。
- Cloud Console の [認証情報] ページで API キーを生成します。詳しい手順については、こちらの動画またはドキュメントをご覧ください。Google Maps Platform へのすべてのリクエストで API キーが必要になります。
Maps 3D SDK for iOS を有効にする
Maps 3D SDK for iOS は、コンソールの [Google Maps Platform] > [API とサービス] メニューリンクから確認できます。
[有効にする] をクリックして、選択したプロジェクトで API を有効にします。
3. 基本的な SwiftUI アプリを作成する
注: 各ステップの解答コードは、GitHub の Codelab サンプルアプリ リポジトリで確認できます。
Xcode で新しいアプリを作成します。
このステップのコードは、GitHub の GoogleMaps3DDemo フォルダにあります。
Xcode を開いて新しいアプリを作成し、SwiftUI を指定します。
アプリを GoogleMaps3DDemo
とし、パッケージ名を com.example.GoogleMaps3DDemo
にします。
GoogleMaps3D ライブラリをプロジェクトにインポートする
Swift Package Manager を使用して SDK をプロジェクトに追加します。
Xcode プロジェクトまたはワークスペースで、[ファイル] > [パッケージの依存関係を追加] に移動します。URL として https://github.com/googlemaps/ios-maps-3d-sdk を入力し、Enter キーを押してパッケージを取得し、[パッケージを追加] をクリックします。
[パッケージ プロダクトの選択] ウィンドウで、指定したメイン ターゲットに GoogleMaps3D
が追加されることを確認します。完了したら、[パッケージを追加] をクリックします。
インストールを確認するには、成果目標の [全般] ペインに移動します。[フレームワーク]、[ライブラリ]、[埋め込みコンテンツ] に、インストールされているパッケージが表示されます。Project Navigator の [Package Dependencies] セクションでパッケージとそのバージョンを確認することもできます。
API キーを追加する
API キーをアプリにハードコードすることもできますが、これはおすすめしません。構成ファイルを追加すると、API キーをシークレットとして保持し、ソース管理にチェックインする必要がなくなります。
プロジェクトのルートフォルダに新しい構成ファイルを作成する
Xcode で、プロジェクト エクスプローラ ウィンドウが表示されていることを確認します。プロジェクトのルートを右クリックし、[テンプレートから新規ファイル] を選択します。[Configuration Settings File] が表示されるまでスクロールします。これを選択して [次へ] をクリックします。ファイルに Config.xcconfig
という名前を付け、プロジェクトのルートフォルダが選択されていることを確認します。[作成] をクリックしてファイルを作成します。
エディタで、構成ファイルに次の行を追加します。MAPS_API_KEY = YOUR_API_KEY
YOUR_API_KEY
を API キーに置き換えます。
この設定を Info.plist
に追加します。
これを行うには、プロジェクトのルートを選択して [情報] タブをクリックします。
値が $(MAPS_API_KEY)
の MAPS_API_KEY
という新しいプロパティを追加します。
サンプルアプリのコードには、このプロパティを指定する Info.plist
ファイルがあります。
地図を追加する
GoogleMaps3DDemoApp.swift
というファイルを開きます。これはアプリのエントリ ポイントとメイン ナビゲーションです。
ContentView()
を呼び出して、Hello World メッセージを表示します。
エディタで ContentView.swift
を開きます。
GoogleMaps3D
の import
ステートメントを追加します。
var body: some View {}
コードブロック内のコードを削除します。body
内に新しい Map()
を宣言します。
Map
を初期化するために必要な最小構成は MapMode
です。使用可能な値は次の 2 つです。
.hybrid
- 道路とラベルが付いた衛星画像.satellite
- 衛星画像のみ。
ぜひ.hybrid
にご用命ください。
ContentView.swift
ファイルは次のようになります。
import GoogleMaps3D
import SwiftUI
@main
struct ContentView: View {
var body: some View {
Map(mode: .hybrid)
}
}
API キーを設定します。
API キーは、地図の初期化前に設定する必要があります。
これを行うには、地図を含む View
の init()
イベント ハンドラで Map.apiKey
を設定します。ContentView()
を呼び出す前に GoogleMaps3DDemoApp.swift
で設定することもできます。
GoogleMaps3DDemoApp.swift
で、WindowGroup
の onAppear
イベント ハンドラに Map.apiKey
を設定します。
構成ファイルから API キーを取得する
Bundle.main.infoDictionary
を使用して、構成ファイルで作成した MAPS_API_KEY
設定にアクセスします。
import GoogleMaps3D
import SwiftUI
@main
struct GoogleMaps3DDemoApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.onAppear {
guard let infoDictionary: [String: Any] = Bundle.main.infoDictionary else {
fatalError("Info.plist not found")
}
guard let apiKey: String = infoDictionary["MAPS_API_KEY"] as? String else {
fatalError("MAPS_API_KEY not set in Info.plist")
}
Map.apiKey = apiKey
}
}
}
アプリをビルドして実行し、正しく読み込まれることを確認します。地球の地図が表示されます。
4. カメラを使用して地図ビューを操作する
カメラの状態オブジェクトを作成する
3D マップビューは Camera
クラスによって制御されます。このステップでは、位置、高度、方位、傾斜、ロール、範囲を指定して地図ビューをカスタマイズする方法を学習します。
カメラの設定を保存する Helpers クラスを作成する
MapHelpers.swift
という名前の新しい空のファイルを追加します。新しいファイルで GoogleMaps3D
をインポートし、Camera
クラスに拡張機能を追加します。sanFrancisco
という変数を追加します。この変数を新しい Camera
オブジェクトとして初期化します。カメラを latitude: 37.39, longitude: -122.08
に配置します。
import GoogleMaps3D
extension Camera {
public static var sanFrancisco: Camera = .init(latitude: 37.39, longitude: -122.08)
}
アプリに新しいビューを追加する
CameraDemo.swift
という名前の新しいファイルを作成します。新しい SwiftUI ビューの基本的なアウトラインをファイルに追加します。
Camera
型の camera
という @State
変数を追加します。先ほど定義した sanFrancisco
カメラに初期化します。
@State
を使用すると、地図をカメラの状態にバインドし、信頼できる情報源として使用できます。
@State var camera: Camera = .sanFrancisco
camera
プロパティを含めるように Map()
関数呼び出しを変更します。カメラの状態バインディング $camera
を使用して、camera
プロパティを Camera @State
オブジェクト(.sanFrancisco
)に初期化します。
import SwiftUI
import GoogleMaps3D
struct CameraDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid)
}
}
}
基本的なナビゲーション UI をアプリに追加する
アプリのメイン エントリ ポイント GoogleMaps3DDemoApp.swift
に NavigationView
を追加します。
これにより、ユーザーはデモのリストを表示し、各デモをクリックして開くことができます。
GoogleMaps3DDemoApp.swift
を編集して、新しい NavigationView
を追加します。
2 つの NavigationLink
宣言を含む List
を追加します。
最初の NavigationLink
は、Text
の説明 Basic Map
で ContentView()
を開きます。
2 番目の NavigationLink
で CameraDemo()
が開きます。
...
NavigationView {
List {
NavigationLink(destination: ContentView()) {
Text("Basic Map")
}
NavigationLink(destination: CameraDemo()) {
Text("Camera Demo")
}
}
}
...
Xcode プレビューを追加する
プレビューは、アプリの変更を加える際にアプリを表示して操作できる強力な Xcode 機能です。
プレビューを追加するには、CameraDemo.swift
を開きます。struct
の外側に #Preview {}
コードブロックを追加します。
#Preview {
CameraDemo()
}
Xcode でプレビュー パネルを開くか、更新します。地図にサンフランシスコが表示されます。
カスタム 3D ビューを設定する
追加のパラメータを指定してカメラを制御できます。
heading
: カメラを向ける北からの方位(度数)。tilt
: 傾斜角度(度)。0 は真上、90 は水平を向きます。roll
: カメラの垂直面を中心としたロール角(度)range
: 緯度と経度の位置からのカメラの距離(メートル単位)altitude
: カメラの海抜
これらの追加パラメータを指定しない場合、デフォルト値が使用されます。
カメラビューに 3D データを多く表示するには、近づいて傾斜したビューを表示するように初期パラメータを設定します。
MapHelpers.swift
で定義した Camera
を編集して、altitude
、heading
、tilt
、roll
、range
の値を含めます。
public static var sanFrancisco: Camera = .init(
latitude: 37.7845812,
longitude: -122.3660241,
altitude: 585,
heading: 288.0,
tilt: 75.0,
roll: 0.0,
range: 100)
アプリをビルドして実行し、新しい 3D ビューを表示して探索します。
5. 基本的なカメラ アニメーション
ここまでは、カメラを使用して、傾斜、高度、方位、範囲を指定した単一の位置を指定してきました。このステップでは、これらのプロパティを初期状態から新しい状態にアニメーション化することで、カメラビューを移動する方法を学習します。
場所に移動する
Map.flyCameraTo()
メソッドを使用して、カメラを最初の位置から新しい位置にアニメーション化します。
flyCameraTo()
メソッドは、次のパラメータを取ります。
- 終点を表す
Camera
。 duration
: アニメーションの実行時間(秒)。trigger
: 状態が変化したときにアニメーションをトリガーするオブザーバブル オブジェクト。completion
: アニメーションの完了時に実行されるコード。
目的地を定義する
MapHelpers.swift
ファイルを開きます。
シアトルを表示する新しいカメラ オブジェクトを定義します。
public static var seattle: Camera = .init(latitude:
47.6210296,longitude: -122.3496903, heading: 149.0, tilt: 77.0, roll: 0.0, range: 4000)
アニメーションをトリガーするボタンを追加します。
CameraDemo.swift
を開きます。struct
内に新しいブール変数を宣言します。
animate
という名前にして、初期値を false
にします。
@State private var animate: Bool = false
VStack
の下に Button
を追加します。Button
は地図アニメーションを開始します。
Button
に適切な Text
(「飛行を開始」など)を指定します。
import SwiftUI
import GoogleMaps3D
struct CameraDemo: View {
@State var camera:Camera = .sanFrancisco
@State private var animate: Bool = false
var body: some View {
VStack{
Map(camera: $camera, mode: .hybrid)
Button("Start Flying") {
}
}
}
}
Button クロージャーに、animate
変数の状態を切り替えるコードを追加します。
Button("Start Flying") {
animate.toggle()
}
アニメーションを開始します。
animate
変数の状態が変化したときに flyCameraTo()
アニメーションをトリガーするコードを追加します。
var body: some View {
VStack{
Map(camera: $camera, mode: .hybrid)
.flyCameraTo(
.seattle,
duration: 5,
trigger: animate,
completion: { }
)
Button("Start Flying") {
animate.toggle()
}
}
}
特定の場所を飛行する
場所を飛行するには、Map.flyCameraAround()
メソッドを使用します。このメソッドは、次のパラメータを使用します。
- ロケーションとビューを定義する
Camera
。 duration
(秒単位)rounds
: アニメーションを繰り返す回数。trigger
: アニメーションをトリガーするオブザーバブル オブジェクト。callback
: アニメーションの実行時に実行されるコード。
flyAround
という新しい @State
変数を定義し、初期値を false
に設定します。
その後、flyCameraTo()
メソッド呼び出しの直後に flyCameraAround()
の呼び出しを追加します。
ビューがスムーズに変化するように、飛行時間は比較的長くする必要があります。
flyCameraTo()
が完了したら、トリガー オブジェクトの状態を変更して、flyCameraAround()
アニメーションをトリガーしてください。
コードの内容は次のようになります。
import SwiftUI
import GoogleMaps3D
struct CameraDemo: View {
@State var camera:Camera = .sanFrancisco
@State private var animate: Bool = false
@State private var flyAround: Bool = false
var body: some View {
VStack{
Map(camera: $camera, mode: .hybrid)
.flyCameraTo(
.seattle,
duration: 5,
trigger: animate,
completion: { flyAround = true }
)
.flyCameraAround(
.seattle,
duration: 15,
rounds: 0.5,
trigger: flyAround,
callback: { }
)
Button("Start Flying") {
animate.toggle()
}
}
}
}
#Preview {
CameraDemo()
}
アプリをプレビューするか実行して、flyCameraTo()
アニメーションが完了するとカメラが目的地を周回することを確認します。
6. 地図にマーカーを追加します。
このステップでは、地図上にマーカーピンを描画する方法を学習します。
Marker
オブジェクトを作成して地図に追加します。SDK は、マーカーにデフォルトのアイコンを使用します。最後に、マーカーの高さやその他のプロパティを調整して、表示方法を変更します。
マーカーのデモ用に新しい SwiftUI ビューを作成します。
プロジェクトに新しい Swift ファイルを追加します。MarkerDemo.swift
という名前を付けます。
SwiftUI ビューのアウトラインを追加し、CameraDemo
で行ったように地図を初期化します。
import SwiftUI
import GoogleMaps3D
struct MarkerDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid)
}
}
}
Marker オブジェクトを初期化する
mapMarker
という新しいマーカー変数を宣言します。MarkerDemo.swift
の struct
コードブロックの上部。
定義は、camera
宣言の下の行に配置します。このサンプルコードでは、使用可能なすべてのプロパティを初期化します。
@State var mapMarker: Marker = .init(
position: .init(
latitude: 37.8044862,
longitude: -122.4301493,
altitude: 0.0),
altitudeMode: .absolute,
collisionBehavior: .required,
extruded: false,
drawsWhenOccluded: true,
sizePreserved: true,
zIndex: 0,
label: "Test"
)
地図にマーカーを追加します。
マーカーを描画するには、地図の作成時に呼び出されるクロージャーにマーカーを追加します。
struct MarkerDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
mapMarker
}
}
}
}
GoogleMaps3DDemoApp.swift
に新しい NavigationLink
を追加し、リンク先を MarkerDemo()
、Text
を「Marker Demo」に設定します。
...
NavigationView {
List {
NavigationLink(destination: Map()) {
Text("Basic Map")
}
NavigationLink(destination: CameraDemo()) {
Text("Camera Demo")
}
NavigationLink(destination: MarkerDemo()) {
Text("Marker Demo")
}
}
}
...
アプリをプレビューして実行する
プレビューを更新するか、アプリを実行してマーカーを確認します。
押し出したマーカー
マーカーは、altitude
と altitudeMode
を使用して地面または 3D メッシュの上に配置できます。
MarkerDemo.swift
の mapMarker
宣言を、extrudedMarker
という新しい Marker
変数にコピーします。
altitude
にゼロ以外の値を設定します。50 で十分です。
altitudeMode
を .relativeToMesh
に変更し、extruded
を true
に設定します。次のコード スニペットの latitude
と longitude
を使用して、超高層ビルの上にマーカーを配置します。
@State var extrudedMarker: Marker = .init(
position: .init(
latitude: 37.78980534,
longitude: -122.3969349,
altitude: 50.0),
altitudeMode: .relativeToMesh,
collisionBehavior: .required,
extruded: true,
drawsWhenOccluded: true,
sizePreserved: true,
zIndex: 0,
label: "Extruded"
)
アプリをもう一度実行するか、プレビューします。マーカーは 3D 建物の上に表示されます。
7. 地図にモデルを追加します。
Model
は、Marker
と同じ方法で追加できます。URL でアクセスできるモデルファイル、またはプロジェクトにローカル ファイルとして追加できるモデルファイルが必要です。このステップでは、この Codelab の GitHub リポジトリからダウンロードできるローカル ファイルを使用します。
モデルファイルをプロジェクトに追加する
Xcode プロジェクトに Models
という名前の新しいフォルダを作成します。
GitHub のサンプルアプリ リポジトリからモデルをダウンロードします。Xcode プロジェクト ビューの新しいフォルダにドラッグして、プロジェクトに追加します。
ターゲットをアプリの主なターゲットに設定してください。
プロジェクトの [Build Phases] > [Copy Bundle Resources] の設定を確認します。モデルファイルは、バンドルにコピーされたリソースのリストに含まれている必要があります。ない場合は、[+] をクリックして追加します。
モデルをアプリに追加します。
ModelDemo.swift
という名前の新しい SwiftUI ファイルを作成します。
前の手順と同様に、SwiftUI
と GoogleMaps3D
に import
ステートメントを追加します。
body
の VStack
内に Map
を宣言します。
import SwiftUI
import GoogleMaps3D
struct ModelDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
}
}
}
}
バンドルからモデルパスを取得します。このコードは struct
の外側に追加します。
private let fileUrl = Bundle.main.url(forResource: "balloon", withExtension: "glb")
構造体内にモデルの変数を宣言します。
fileUrl
が指定されていない場合に備えて、デフォルト値を指定します。
@State var balloonModel: Model = .init(
position: .init(
latitude: 37.791376,
longitude: -122.397571,
altitude: 300.0),
url: URL(fileURLWithPath: fileUrl?.relativePath ?? ""),
altitudeMode: .absolute,
scale: .init(x: 5, y: 5, z: 5),
orientation: .init(heading: 0, tilt: 0, roll: 0)
)
3. 地図でモデルを使用します。
Marker
の追加と同様に、Map
宣言で Model
への参照を指定します。
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
balloonModel
}
}
}
アプリをプレビューして実行する
GoogleMaps3DDemoApp.swift
に新しい NavigationLink
を追加します。宛先は ModelDemo()
、Text
は「Model Demo」です。
...
NavigationLink(destination: ModelDemo()) {
Text("Model Demo")
}
...
プレビューを更新するか、アプリを実行してモデルを表示します。
8. 地図上にラインとポリゴンを描画します。
このステップでは、3D 地図にラインとポリゴンのシェイプを追加する方法を学びます。
わかりやすくするために、シェイプを LatLngAltitude
オブジェクトの配列として定義します。実際のアプリケーションでは、データはファイル、API 呼び出し、データベースから読み込まれます。
シェイプ データを管理するシェイプ オブジェクトを作成します。
サンフランシスコのダウンタウンを対象とする新しい Camera
定義を MapHelpers.swift
に追加します。
public static var downtownSanFrancisco: Camera = .init(latitude: 37.7905, longitude: -122.3989, heading: 25, tilt: 71, range: 2500)
プロジェクトに ShapesDemo.swift
という新しいファイルを追加します。View
プロトコルを実装する ShapesDemo
という struct
を追加し、body
を追加します。
struct ShapesDemo: View {
@State var camera: Camera = .downtownSanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
}
}
}
}
シェイプデータを管理するために使用するクラスは Polyline
と Polygon
です。ShapesDemo.swift
を開き、次のように struct
に追加します。
var polyline: Polyline = .init(coordinates: [
LatLngAltitude(latitude: 37.80515638571346, longitude: -122.4032569467164, altitude: 0),
LatLngAltitude(latitude: 37.80337073509504, longitude: -122.4012878349353, altitude: 0),
LatLngAltitude(latitude: 37.79925208843463, longitude: -122.3976697250461, altitude: 0),
LatLngAltitude(latitude: 37.7989102378512, longitude: -122.3983408725656, altitude: 0),
LatLngAltitude(latitude: 37.79887832784348, longitude: -122.3987094864192, altitude: 0),
LatLngAltitude(latitude: 37.79786443410338, longitude: -122.4066878788802, altitude: 0),
LatLngAltitude(latitude: 37.79549248916587, longitude: -122.4032992702785, altitude: 0),
LatLngAltitude(latitude: 37.78861484290265, longitude: -122.4019489189814, altitude: 0),
LatLngAltitude(latitude: 37.78618687561075, longitude: -122.398969592545, altitude: 0),
LatLngAltitude(latitude: 37.7892310309145, longitude: -122.3951458683092, altitude: 0),
LatLngAltitude(latitude: 37.7916358762409, longitude: -122.3981969390652, altitude: 0)
])
.stroke(GoogleMaps3D.Polyline.StrokeStyle(
strokeColor: UIColor(red: 0.09803921568627451, green: 0.403921568627451, blue: 0.8235294117647058, alpha: 1),
strokeWidth: 10.0,
outerColor: .white,
outerWidth: 0.2
))
.contour(GoogleMaps3D.Polyline.ContourStyle(isGeodesic: true))
var originPolygon: Polygon = .init(outerCoordinates: [
LatLngAltitude(latitude: 37.79165766856578, longitude: -122.3983762901255, altitude: 300),
LatLngAltitude(latitude: 37.7915324439261, longitude: -122.3982171091383, altitude: 300),
LatLngAltitude(latitude: 37.79166617650914, longitude: -122.3980478493319, altitude: 300),
LatLngAltitude(latitude: 37.79178986470217, longitude: -122.3982041104199, altitude: 300),
LatLngAltitude(latitude: 37.79165766856578, longitude: -122.3983762901255, altitude: 300 )
],
altitudeMode: .relativeToGround)
.style(GoogleMaps3D.Polygon.StyleOptions(fillColor:.green, extruded: true) )
var destinationPolygon: Polygon = .init(outerCoordinates: [
LatLngAltitude(latitude: 37.80515661739527, longitude: -122.4034307490334, altitude: 300),
LatLngAltitude(latitude: 37.80503794515428, longitude: -122.4032633416024, altitude: 300),
LatLngAltitude(latitude: 37.80517850164195, longitude: -122.4031056058006, altitude: 300),
LatLngAltitude(latitude: 37.80529346901115, longitude: -122.4032622466595, altitude: 300),
LatLngAltitude(latitude: 37.80515661739527, longitude: -122.4034307490334, altitude: 300 )
],
altitudeMode: .relativeToGround)
.style(GoogleMaps3D.Polygon.StyleOptions(fillColor:.red, extruded: true) )
使用されている初期化パラメータに注意してください。
altitudeMode: .relativeToGround
は、ポリゴンを地面から特定の高さまで押し出すために使用します。altitudeMode: .clampToGround
は、ポリラインが地球の表面の形状に沿うようにするために使用されます。.init()
が呼び出された後にstyleOptions()
へのメソッド呼び出しを連結して、Polygon
オブジェクトにスタイルが設定される
地図にシェイプを追加する
前の手順と同様に、シェイプは Map
閉じに直接追加できます。VStack
内に Map
を作成します。
...
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
polyline
originPolygon
destinationPolygon
}
}
}
...
アプリをプレビューして実行する
プレビュー コードを追加し、Xcode のプレビュー ペインでアプリを検査します。
#Preview {
ShapesDemo()
}
アプリを実行するには、新しいデモビューを開く NavigationLink
を GoogleMaps3DDemoApp.swift
に追加します。
...
NavigationLink(destination: ShapesDemo()) {
Text("Shapes Demo")
}
...
アプリを実行して、追加したシェイプを確認します。
9. 場所マーカーのタップ イベントを処理する
このステップでは、ユーザーがプレイス マーカーをタップしたときに応答する方法について説明します。
注: 地図上に場所マーカーを表示するには、MapMode
を .hybrid
に設定する必要があります。
タップ処理を行うには、Map.onPlaceTap
メソッドを実装する必要があります。
onPlaceTap
イベントは、タップされたプレイス マーカーのプレイス ID を取得できる PlaceTapInfo
オブジェクトを提供します。
プレイス ID を使用して、Places SDK または Places API で詳細情報を検索できます。
新しい Swift ビューを追加する
PlaceTapDemo.swift
という名前の新しい Swift ファイルに次のコードを追加します。
import GoogleMaps3D
import SwiftUI
struct PlaceTapDemo: View {
@State var camera: Camera = .sanFrancisco
@State var isPresented = false
@State var tapInfo: PlaceTapInfo?
var body: some View {
Map(camera: $camera, mode: .hybrid)
.onPlaceTap { tapInfo in
self.tapInfo = tapInfo
isPresented.toggle()
}
.alert(
"Place tapped - \(tapInfo?.placeId ?? "nil")",
isPresented: $isPresented,
actions: { Button("OK") {} }
)
}
}
#Preview {
PlaceTapDemo()
}
アプリをプレビューして実行する
[プレビュー] ペインを開いてアプリをプレビューします。
アプリを実行するには、GoogleMaps3DDemoApp.swift
に新しい NavigationLink
を追加します。
...
NavigationLink(destination: PlaceTapDemo()) {
Text("Place Tap Demo")
}
...
10. (省略可)さらに活用する
高度なカメラのアニメーション
飛行シミュレーターやハイキングやランニングの再生など、一部のユースケースでは、位置情報やカメラ状態のシーケンスまたはリストに沿ってスムーズにアニメーション化する必要があります。
このステップでは、ファイルから場所のリストを読み込み、各場所を順番にアニメーション化する方法について説明します。
一連の位置情報を含むファイルを読み込みます。
GitHub のサンプルアプリ リポジトリから flightpath.json
をダウンロードします。
Xcode プロジェクトに JSON
という名前の新しいフォルダを作成します。
flightpath.json
を Xcode の JSON
フォルダにドラッグします。
ターゲットをアプリのメイン ターゲットに設定します。プロジェクトの [バンドル リソースのコピー] 設定にこのファイルが含まれていることを確認します。
アプリに FlightPathData.swift
と FlightDataLoader.swift
という 2 つの新しい Swift ファイルを作成します。
次のコードをアプリにコピーします。このコードは、flighpath.json というローカル ファイルを読み取り、JSON としてデコードする構造体とクラスを作成します。
FlightPathData
構造体と FlightPathLocation
構造体は、JSON ファイル内のデータ構造を Swift オブジェクトとして表します。
FlightDataLoader
クラスは、ファイルからデータを読み取り、デコードします。ObservableObject
プロトコルを実装しているため、アプリでデータの変更を監視できます。
解析されたデータは、公開されたプロパティを介して公開されます。
FlightPaths.swift
import GoogleMaps3D
struct FlightPathData: Decodable {
let flight: [FlightPathLocation]
}
struct FlightPathLocation: Decodable {
let timestamp: Int64
let latitude: Double
let longitude: Double
let altitude: Double
let bearing: Double
let speed: Double
}
FlightDataLoader.swift
import Foundation
public class FlightDataLoader : ObservableObject {
@Published var flightPathData: FlightPathData = FlightPathData(flight:[])
@Published var isLoaded: Bool = false
public init() {
load("flightpath.json")
}
public func load(_ path: String) {
if let url = Bundle.main.url(forResource: path, withExtension: nil){
if let data = try? Data(contentsOf: url){
let jsondecoder = JSONDecoder()
do{
let result = try jsondecoder.decode(FlightPathData.self, from: data)
flightPathData = result
isLoaded = true
}
catch {
print("Error trying to load or parse the JSON file.")
}
}
}
}
}
各場所に沿ってカメラをアニメーション化する
一連のステップ間でカメラをアニメーション化するには、KeyframeAnimator
を使用します。
各 Keyframe
は CubicKeyframe
として作成されるため、カメラの状態の変化がスムーズにアニメーション化されます。
flyCameraTo()
を使用すると、ビューが各ロケーション間で「バウンス」します。
まず、MapHelpers.swift
で「innsbruck」というカメラを宣言します。
public static var innsbruck: Camera = .init(
latitude: 47.263,
longitude: 11.3704,
altitude: 640.08,
heading: 237,
tilt: 80.0,
roll: 0.0,
range: 200)
次に、FlyAlongRoute.swift
という新しいファイルに新しいビューを設定します。
SwiftUI
と GoogleMaps3D
をインポートします。VStack
内に Map
と Button
を追加します。ブール値 animation
変数の状態を切り替えるように Button
を設定します。
FlightDataLoader
の State
オブジェクトを宣言します。このオブジェクトは、初期化時に JSON ファイルを読み込みます。
import GoogleMaps3D
import SwiftUI
struct FlyAlongRoute: View {
@State private var camera: Camera = .innsbruck
@State private var flyToDuration: TimeInterval = 5
@State var animation: Bool = true
@StateObject var flightData: FlightDataLoader = FlightDataLoader()
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid)
Button("Fly Along Route"){
animation.toggle()
}
}
}
}
キーフレームを作成する
基本的なプロセスは、アニメーション シーケンスの新しいフレームを返す関数を作成することです。新しいフレームごとに、アニメーターがアニメーション化する次のカメラ状態が定義されます。この関数を作成したら、ファイル内の各ロケーションを順番に指定して呼び出します。
FlyAlongRoute
構造体に 2 つの関数を追加します。関数 makeKeyFrame
は、カメラの状態を含む CubicKeyframe
を返します。関数 makeCamera
は、飛行データ シーケンス内のステップを受け取り、そのステップを表す Camera
オブジェクトを返します。
func makeKeyFrame(step: FlightPathLocation) -> CubicKeyframe<Camera> {
return CubicKeyframe(
makeCamera(step: step),
duration: flyToDuration
)
}
func makeCamera(step: FlightPathLocation) -> Camera {
return .init(
latitude: step.latitude,
longitude: step.longitude,
altitude: step.altitude,
heading: step.bearing,
tilt: 75,
roll: 0,
range: 200
)
}
アニメーションをまとめる
Map
の初期化後に keyframeAnimator
を呼び出して、初期値を設定します。
飛行経路の最初の位置に基づくカメラの初期状態が必要です。
アニメーションは、変数の状態変化に基づいてトリガーする必要があります。
keyframeAnimator
のコンテンツはマップである必要があります。
実際のキーフレームのリストは、飛行経路の各位置をループして生成されます。
VStack {
Map(camera: $camera, mode: .hybrid)
.keyframeAnimator(
initialValue: makeCamera(step: flightData.flightPathData.flight[0]),
trigger: animation,
content: { view, value in
Map(camera: .constant(value), mode: .hybrid)
},
keyframes: { _ in
KeyframeTrack(content: {
for i in 1...flightData.flightPathData.flight.count-1 {
makeKeyFrame(step: flightData.flightPathData.flight[i])
}
})
}
)
}
アプリをプレビューして実行します。
プレビュー パネルを開いてビューをプレビューします。
FlightPathDemo()
をデスティネーションとする新しい NavigationLink
を GoogleMaps3DDemoApp.swift
に追加し、アプリを実行して試します。
11. 完了
次のアプリケーションが正常に作成されました。
- 基本的な 3D 地図をアプリに追加します。
- マーカー、ライン、ポリゴン、モデルを地図に追加します。
- 地図上を飛行したり、特定の場所を飛行したりするカメラを制御するコードを実装します。
学習した内容
- Xcode SwiftUI アプリに
GoogleMaps3D
パッケージを追加する方法。 - API キーとデフォルト ビューを使用して 3D 地図を初期化する方法。
- マーカー、3D モデル、ライン、ポリゴンを地図に追加する方法。
- カメラを制御して別の場所への移動をアニメーション化する方法。
- プレイス マーカーのクリック イベントを処理する方法。
次のステップ
- Maps 3D SDK for iOS でできることについて詳しくは、デベロッパー ガイドをご覧ください。
- 皆様のお役に立つコンテンツをご提供できるよう、以下のアンケートにご協力ください。