1. 准备工作
此 Codelab 介绍了如何使用 Maps 3D SDK for iOS 在 SwiftUI 中创建 3D 地图应用。
您会了解到以下内容:
- 如何控制相机查看地点和在 Google 地图上飞行。
- 如何添加标记和模型
- 如何绘制线条和多边形
- 如何处理用户对地点标记的点击。
前提条件
- 启用了结算功能的 Google 控制台项目
- API 密钥,可选择限制为仅适用于 iOS 版 Maps 3D SDK。
- 具备使用 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 密钥。
启用 iOS 版 Maps 3D SDK
您可以通过控制台中的“Google Maps Platform”>“API 和服务”菜单链接找到 Maps 3D SDK for iOS。
点击“启用”以在所选项目中启用该 API。
3. 创建一个基本的 SwiftUI 应用
注意:您可以在 GitHub 上的 Codelab 示例应用代码库中找到每一步的解决方案代码。
在 Xcode 中创建新应用。
此步骤的代码可以在 GitHub 上的 GoogleMaps3DDemo 文件夹中找到。
打开 Xcode 并创建一个新应用。指定 SwiftUI。
将应用命名为 GoogleMaps3DDemo
,软件包名称为 com.example.GoogleMaps3DDemo
。
将 GoogleMaps3D 库导入您的项目
使用 Swift Package Manager 将 SDK 添加到您的项目中。
在 Xcode 项目或工作区中,依次选择“File”(文件)>“Add Package Dependencies”(添加软件包依赖项)。输入 https://github.com/googlemaps/ios-maps-3d-sdk 作为网址,按 Enter 键提取软件包,然后点击“Add Package”(添加软件包)。
在“选择软件包商品”窗口中,验证 GoogleMaps3D
是否会添加到您指定的主要目标。完成后,点击“添加软件包”。
如需验证安装情况,请前往目标的“常规”窗格。在“框架、库和嵌入内容”中,您应该会看到已安装的软件包。您还可以查看 Project Navigator 的“软件包依赖项”部分,以验证软件包及其版本。
添加您的 API 密钥
您可以将 API 密钥硬编码到应用中,但这不是一种好做法。通过添加配置文件,您可以保留 API 密钥的秘密性,并避免将其签入源代码控制系统。
在项目根文件夹中创建新的配置文件
在 Xcode 中,确保您正在查看项目浏览器窗口。右键点击项目根目录,然后选择“New File from Template”。滚动到“配置设置文件”。选择此选项,然后点击“下一步”。为文件命名为 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 密钥,然后才能初始化 Map。
为此,您可以在包含地图的任何 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
可将 Map 绑定到相机状态,并将其用作可信来源。
@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)
}
}
}
向应用添加基本导航界面
向应用主入口点 GoogleMaps3DDemoApp.swift
添加 NavigationView
。
这样,用户就可以查看演示版列表,并点击每个演示版将其打开。
修改 GoogleMaps3DDemoApp.swift
以添加新的 NavigationView
。
添加一个包含两个 NavigationLink
声明的 List
。
第一个 NavigationLink
应打开带有 Text
说明 Basic Map
的 ContentView()
。
第二个 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"
)
将标记添加到地图中。
如需绘制标记,请将其添加到创建 Map 时调用的闭包中。
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
相同。您需要一个可通过网址访问或添加为项目中的本地文件的模型文件。在此步骤中,我们将使用一个本地文件,您可以从此 Codelab 的 GitHub 代码库下载该文件。
将模型文件添加到项目中
在 Xcode 项目中创建一个名为 Models
的新文件夹。
从 GitHub 示例应用代码库下载模型。将其拖动到 Xcode 项目视图中的新文件夹,将其添加到项目中。
请务必将该目标设置为应用的主要目标。
检查项目的“构建阶段”>“复制软件包资源”设置。模型文件应位于复制到软件包的资源列表中。如果没有,请点击“+”进行添加。
将模型添加到您的应用。
创建一个名为 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 调用或数据库加载。
创建一些形状对象来管理形状数据。
向 MapHelpers.swift
添加一个新的 Camera
定义,用于查看旧金山市区。
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
用于将多边形 extrusion 到地面上方特定的高度。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()
}
如需运行应用,请向 GoogleMaps3DDemoApp.swift
添加一个新的 NavigationLink
,以打开新的演示版 View。
...
NavigationLink(destination: ShapesDemo()) {
Text("Shapes Demo")
}
...
运行应用并探索您添加的形状。
9. 处理地点标记上的点按事件
在此步骤中,您将学习如何响应用户点按地点标记的操作。
注意:如需在地图上查看地点标记,您需要将 MapMode
设置为 .hybrid
。
如需处理点按,需要实现 Map.onPlaceTap
方法。
onPlaceTap
事件会提供一个 PlaceTapInfo
对象,您可以通过该对象获取用户点按的地点标记的地点 ID。
您可以使用地点 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
的新 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
的新文件中设置一个新 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
内容应为 Map。
实际的关键帧列表是通过循环遍历飞行路径中的每个位置生成的。
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])
}
})
}
)
}
预览和运行应用。
打开“预览”窗格以预览视图。
向 GoogleMaps3DDemoApp.swift
添加一个目的地为 FlightPathDemo()
的新 NavigationLink
,然后运行应用进行试用。
11. 恭喜
您已成功构建一款具备以下特征的应用:
- 向应用添加基本 3D 地图。
- 向地图添加标记、线条、多边形和模型。
- 实现代码以控制相机在特定位置周围和地图上飞行。
要点回顾
- 如何将
GoogleMaps3D
软件包添加到 Xcode SwiftUI 应用。 - 如何使用 API 密钥和默认视图初始化 3D 地图。
- 如何向地图添加标记、3D 模型、线条和多边形。
- 如何控制相机以呈现移动到其他位置的动画效果。
- 如何处理地点标记上的点击事件。
后续操作
- 如需详细了解如何使用 iOS 版 Maps 3D SDK,请参阅开发者指南。
- 请回答以下调查问卷,帮助我们为您制作最为有用的内容: