1. 事前準備
本程式碼研究室將說明如何使用 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 地圖平台
如果您尚未建立 Google Cloud Platform 帳戶,以及已啟用帳單功能的專案,請參閱「開始使用 Google 地圖平台」指南,建立帳單帳戶和專案。
- 在 Cloud 控制台中,點選專案下拉式選單,然後選取要用於本程式碼研究室的專案。
- 在 Google Cloud Marketplace 中啟用這個程式碼研究室所需的 Google 地圖平台 API 和 SDK。如要進行這項操作,請按照這部影片或這份說明文件中的步驟操作。
- 在 Cloud 控制台的「憑證」頁面中產生 API 金鑰。您可以按照這部影片或這份說明文件中的步驟操作。所有 Google 地圖平台要求都需要 API 金鑰。
啟用 Maps 3D SDK for iOS
您可以透過控制台的「Google 地圖平台」>「API 和服務」選單連結,找到適用於 iOS 的 Maps 3D SDK。
按一下「啟用」,即可在所選專案中啟用 API。
3. 建立基本 SwiftUI 應用程式
注意:您可以在 GitHub 上的程式碼研究室範例應用程式存放區中,找到各個步驟的解決方案程式碼。
在 Xcode 中建立新應用程式。
您可以在 GitHub 的 GoogleMaps3DDemo 資料夾中找到這個步驟的程式碼。
開啟 Xcode 並建立新的應用程式,指定 SwiftUI。
使用套件名稱 com.example.GoogleMaps3DDemo
呼叫應用程式 GoogleMaps3DDemo
。
將 GoogleMaps3D 程式庫匯入專案
使用 Swift Package Manager 將 SDK 新增至專案。
在 Xcode 專案或工作區中,依序點選「File」>「Add Package Dependencies」。輸入網址 https://github.com/googlemaps/ios-maps-3d-sdk,按下 Enter 鍵即可匯入套件,然後按一下「Add Package」。
在「Choose Package Products」視窗中,確認 GoogleMaps3D
會新增至指定的主要目標。完成後,按一下「新增套件」。
如要驗證安裝作業,請前往目標的「General」窗格。在「架構」、「程式庫」和「嵌入內容」中,您應該會看到已安裝的套件。您也可以查看 Project Navigator 的「Package Dependencies」部分,驗證套件及其版本。
新增 API 金鑰
您可以將 API 金鑰硬式編碼至應用程式,但這不是理想做法。新增設定檔可讓您保密 API 金鑰,並避免將其存入原始碼控管系統。
在專案根目錄中建立新的設定檔
在 Xcode 中,請確認您正在查看專案探索器視窗。在專案根目錄上按一下滑鼠右鍵,然後選取「New File from Template」。捲動至「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
。這個值有兩種可能的值:
.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
,您也可以在 GoogleMaps3DDemoApp.swift
中設定,然後再呼叫 ContentView()
。
在 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)
}
在應用程式中新增 View
建立名為 CameraDemo.swift
的新檔案。將新 SwiftUI 檢視畫面的基本大綱新增至檔案。
新增名為 camera
且型別為 Camera
的 @State
變數。將其初始化為您剛剛定義的 sanFrancisco
相機。
使用 @State
可將地圖繫結至相機狀態,並做為可靠資料來源。
@State var camera: Camera = .sanFrancisco
變更 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
將 NavigationView
新增至主要應用程式進入點 GoogleMaps3DDemoApp.swift
。
這樣一來,使用者就能查看示範影片清單,並點選每部影片來開啟。
編輯 GoogleMaps3DDemoApp.swift
以新增 NavigationView
。
新增含有兩個 NavigationLink
宣告的 List
。
第一個 NavigationLink
應開啟 ContentView()
,並顯示 Text
說明 Basic Map
。
第二個 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 中開啟或重新整理「Preview」窗格。地圖應顯示舊金山。
設定自訂 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
,例如「Start Flying」。
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 會使用預設圖示做為標記。最後,您將調整標記的高度和其他屬性,以變更標記的顯示方式。
為 Marker 示範建立新的 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
}
}
}
}
將新的 NavigationLink
新增至 GoogleMaps3DDemoApp.swift
,目的地為 MarkerDemo()
,Text
則描述為「標記示範」。
...
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
相同。您需要的模型檔案必須可透過網址存取,或可在專案中新增為本機檔案。在這個步驟中,我們會使用本機檔案,您可以從本程式碼研究室的 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
}
}
}
預覽及執行應用程式
將新的 NavigationLink
新增至 GoogleMaps3DDemoApp.swift
,目的地為 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
的檔案。新增名為 ShapesDemo
的 struct
,實作 View
通訊協定,並在其中新增 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」窗格中檢查應用程式。
#Preview {
ShapesDemo()
}
如要執行應用程式,請將新的 NavigationLink
新增至 GoogleMaps3DDemoApp.swift
,以便開啟新的示範 View。
...
NavigationLink(destination: ShapesDemo()) {
Text("Shapes Demo")
}
...
執行應用程式,並查看您新增的形狀。
9. 處理 Place 標記上的輕觸事件
在這個步驟中,您將瞭解如何回應使用者輕觸地點標記的動作。
注意:如要在地圖上顯示地點標記,您必須將 MapMode
設為 .hybrid
。
如要處理輕觸動作,您必須實作 Map.onPlaceTap
方法。
onPlaceTap
事件提供 PlaceTapInfo
物件,您可以透過該物件取得輕觸地點標記的地點 ID。
您可以使用 Place ID 搭配 Places SDK 或 Places API 查詢更多詳細資料。
新增 Swift View
將下列程式碼新增至名為 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()
}
預覽及執行應用程式
開啟「預覽」窗格,即可預覽應用程式。
如要執行應用程式,請將新的 NavigationLink
新增至 GoogleMaps3DDemoApp.swift
。
...
NavigationLink(destination: PlaceTapDemo()) {
Text("Place Tap Demo")
}
...
10. (選用) 進一步瞭解
進階相機動畫
某些用途需要沿著位置或攝影機狀態序列/清單流暢地播放動畫,例如飛行模擬器或重播健行或跑步畫面。
在這個步驟中,您將瞭解如何從檔案載入位置清單,並依序為每個位置製作動畫。
載入包含位置序列的檔案。
從 GitHub 範例應用程式存放區下載 flightpath.json
。
在 Xcode 專案中建立名為 JSON
的新資料夾。
將 flightpath.json
拖曳至 Xcode 中的 JSON
資料夾。
將目標設為應用程式的主要目標。確認專案的「複製套件資源」設定包含此檔案。
在應用程式中建立兩個名為 FlightPathData.swift
和 FlightDataLoader.swift
的新 Swift 檔案。
將下列程式碼複製到應用程式中。這段程式碼會建立結構體和類別,用於讀取名為「flighpath.json」的本機檔案,並將其解碼為 JSON。
FlightPathData
和 FlightPathLocation
結構體會以 Swift 物件呈現 JSON 檔案中的資料結構。
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
的新檔案中設定新的 View。
匯入 SwiftUI
和 GoogleMaps3D
。在 VStack
中新增 Map
和 Button
。設定 Button
來切換布林值 animation
變數的狀態。
為 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
結構體中新增兩個函式。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])
}
})
}
)
}
預覽及執行應用程式。
開啟「預覽」窗格,預覽檢視畫面。
將新的 NavigationLink
新增至 GoogleMaps3DDemoApp.swift
,並設定 FlightPathDemo()
做為目的地,然後執行應用程式來試用。
11. 恭喜
您已成功建構應用程式,該應用程式可執行以下操作:
- 在應用程式中加入基本 3D 地圖。
- 在地圖中加入標記、線條、多邊形和模型。
- 實作程式碼,控制攝影機飛越地圖和特定位置。
您學到的內容
- 如何將
GoogleMaps3D
套件新增至 Xcode SwiftUI 應用程式。 - 如何使用 API 金鑰和預設檢視畫面初始化 3D 地圖。
- 如何在地圖中加入標記、3D 模型、線條和多邊形。
- 如何控制攝影機,讓攝影機動畫移動到其他位置。
- 如何處理地點標記的點擊事件。
後續步驟
- 如要進一步瞭解如何使用 Maps 3D SDK for iOS,請參閱開發人員指南。
- 請回答下列問卷調查,協助我們製作最實用的內容: