1. Прежде чем начать
В этой лабораторной работе вы узнаете, как создать приложение 3D-карт в SwiftUI с использованием Maps 3D SDK для iOS.
Вы узнаете:
- Как управлять камерой, чтобы просматривать локации и летать по карте.
- Как добавлять маркеры и модели
- Как рисовать линии и многоугольники
- Как обрабатывать нажатия пользователей на маркеры мест.
Предпосылки
- Проект Google Console с включенной оплатой
- Ключ API, опционально ограниченный Maps 3D SDK для iOS.
- Базовые знания разработки под iOS с использованием SwiftUI.
Что ты будешь делать?
- Настройте Xcode и добавьте SDK с помощью Swift Package Manager
- Настройте свое приложение для использования ключа API
- Добавьте в свое приложение простую 3D-карту
- Управляйте камерой, чтобы летать в определенные места и вокруг них
- Добавьте на карту маркеры, линии, полигоны и модели
Что вам понадобится
- Xcode 15 или более поздняя версия.
2. Приступайте к настройке
Для следующего шага включения вам потребуется включить Maps 3D SDK для iOS.
Настройте платформу Google Maps
Если у вас еще нет учетной записи Google Cloud Platform и проекта с включенной оплатой, ознакомьтесь с руководством « Начало работы с платформой Google Карт», чтобы создать учетную запись для выставления счетов и проект.
- В Cloud Console щелкните раскрывающееся меню проектов и выберите проект, который вы хотите использовать для этой кодовой лаборатории.
- Включите API и SDK Google Maps Platform, необходимые для этой кодовой лаборатории в Google Cloud Marketplace . Для этого выполните действия, описанные в этом видео или в этой документации .
- Сгенерируйте ключ API на странице Credentials в Cloud Console. Вы можете следовать инструкциям в этом видео или в этой документации . Для всех запросов к Google Maps Platform требуется ключ API.
Включить Maps 3D SDK для iOS
Maps 3D SDK для iOS можно найти, воспользовавшись ссылкой в меню «Платформа Google Карт» > «API и службы» в консоли.
Нажмите «Включить», чтобы включить API в выбранном проекте.
3. Создайте базовое приложение SwiftUI
Примечание: код решения для каждого шага можно найти в репозитории примера приложения codelab на GitHub .
Создайте новое приложение в Xcode.
Код для этого шага можно найти в папке GoogleMaps3DDemo на GitHub.
Откройте Xcode и создайте новое приложение. Укажите SwiftUI.
Назовите свое приложение GoogleMaps3DDemo
, указав имя пакета com.example.GoogleMaps3DDemo
.
Импортируйте библиотеку GoogleMaps3D в свой проект
Добавьте SDK в свой проект с помощью Swift Package Manager.
В вашем проекте Xcode или рабочей области перейдите в File > Add Package Dependencies. Введите https://github.com/googlemaps/ios-maps-3d-sdk в качестве URL, нажмите Enter, чтобы загрузить пакет, и нажмите «Add Package».
В окне Choose Package Products убедитесь, что GoogleMaps3D
будет добавлен в вашу назначенную основную цель. После завершения нажмите Add Package.
Чтобы проверить установку, перейдите на панель General вашего целевого объекта. В разделе Frameworks, Libraries и Embedded Content вы должны увидеть установленные пакеты. Вы также можете просмотреть раздел Package Dependencies в Project Navigator, чтобы проверить пакет и его версию.
Добавьте свой ключ API
Вы можете жестко закодировать свой ключ API в приложении, но это не очень хорошая практика. Добавление файла конфигурации позволяет вам сохранить свой ключ API в секрете и избежать его проверки в системе контроля версий.
Создайте новый файл конфигурации в корневой папке проекта.
В Xcode убедитесь, что вы просматриваете окно обозревателя проектов. Щелкните правой кнопкой мыши по корневому каталогу проекта и выберите «Новый файл из шаблона». Прокрутите, пока не увидите «Файл настроек конфигурации». Выберите его и нажмите «Далее». Назовите файл 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
в редакторе.
Добавьте оператор import
для GoogleMaps3D
.
Удалить код внутри var body: some View {}
code block. Объявить новый Map()
внутри body
.
Минимальная конфигурация, необходимая для инициализации 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.apiKey
в обработчике событий init()
любого View
, содержащего Map. Вы также можете установить его в GoogleMaps3DDemoApp.swift
до того, как он вызовет ContentView()
.
В GoogleMaps3DDemoApp.swift
установите Map.apiKey
в обработчике событий onAppear
WindowGroup
.
Получите свой ключ 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 View.
Добавьте переменную @State
с именем camera
типа Camera
. Инициализируйте ее для камеры sanFrancisco
, которую вы только что определили.
Использование @State
позволяет привязать карту к состоянию камеры и использовать ее в качестве источника достоверных данных.
@State var camera: Camera = .sanFrancisco
Измените вызов функции Map()
, чтобы включить свойство camera
. Используйте привязку состояния камеры $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)
}
}
}
Добавьте базовый навигационный интерфейс в свое приложение
Добавьте NavigationView
к основной точке входа приложения, GoogleMaps3DDemoApp.swift
.
Это позволит пользователям просматривать список демонстраций и открывать каждую из них, нажимая на нее.
Отредактируйте GoogleMaps3DDemoApp.swift
, чтобы добавить новый NavigationView
.
Добавьте List
, содержащий два объявления NavigationLink
.
Первая 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
. Добавьте блок кода #Preview {}
за пределами struct
.
#Preview {
CameraDemo()
}
Откройте или обновите панель предварительного просмотра в Xcode. На карте должен быть показан Сан-Франциско.
Настройте пользовательские 3D-виды
Вы можете указать дополнительные параметры для управления камерой:
-
heading
: азимут в градусах от севера, куда следует направить камеру. -
tilt
: угол наклона в градусах, где 0 соответствует направлению прямо над головой, а 90 — направлению горизонтально. -
roll
: угол крена вокруг вертикальной плоскости камеры, в градусах -
range
: расстояние в метрах от камеры до местоположения по широте и долготе -
altitude
: высота камеры над уровнем моря
Если вы не укажете ни один из этих дополнительных параметров, будут использованы значения по умолчанию.
Чтобы камера отображала больше трехмерных данных, установите начальные параметры для отображения более близкого наклонного вида.
Отредактируйте Camera
которую вы определили в MapHelpers.swift
, включив в нее значения 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
Добавьте Button
под VStack
. 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") {
}
}
}
}
В замыкании кнопки добавьте код для переключения состояния animate
переменной.
Button("Start Flying") {
animate.toggle()
}
Запустите анимацию.
Добавьте код для запуска анимации flyCameraTo()
при изменении состояния переменной animate
.
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
: код, который будет выполнен при запуске анимации.
Определите новую переменную @State
с именем flyAround
и начальным значением false
.
Сделав это, добавьте вызов flyCameraAround()
сразу после вызова метода flyCameraTo()
.
Продолжительность облета должна быть относительно большой, чтобы вид менялся плавно.
Обязательно запустите анимацию flyCameraAround()
, изменив состояние объекта-триггера после завершения flyCameraTo()
.
Ваш код должен выглядеть примерно так.
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)
}
}
}
Инициализировать объект маркера
Объявите новую переменную маркера с именем mapMarker
. В верхней части блока кода struct
в MarkerDemo.swift
.
Поместите определение в строку ниже вашего объявления 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
.
Скопируйте объявление mapMarker
в MarkerDemo.swift
в новую переменную Marker
с именем extrudedMarker
.
Установите ненулевое значение 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 или который можно добавить как локальный файл в ваш проект. Для этого шага мы будем использовать локальный файл, который можно загрузить из репозитория GitHub для этой кодовой лаборатории.
Добавьте файл модели в свой проект
Создайте новую папку в вашем проекте Xcode под названием Models
.
Загрузите модель из репозитория примеров приложений GitHub. Добавьте ее в свой проект, перетащив ее в новую папку в представлении проекта Xcode.
Убедитесь, что вы установили цель, которая будет основной целью вашего приложения.
Проверьте настройки Build Phases > Copy Bundle Resources для вашего проекта. Файл модели должен быть в списке ресурсов, скопированных в бандл. Если его там нет, нажмите «+», чтобы добавить его.
Добавьте модель в свое приложение.
Создайте новый файл SwiftUI с именем ModelDemo.swift
.
Добавьте операторы import
для SwiftUI
и GoogleMaps3D
, как в предыдущих шагах.
Объявите Map
внутри VStack
в вашем body
.
import SwiftUI
import GoogleMaps3D
struct ModelDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
}
}
}
}
Получите путь к модели из вашего Bundle. Добавьте код для этого за пределы 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
, просто укажите ссылку на вашу Model
в объявлении Map
.
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
. Добавьте struct
с именем ShapesDemo
, которая реализует протокол 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
используется для того, чтобы полилиния следовала форме земной поверхности. - стили устанавливаются для объектов
Polygon
путем присоединения вызова метода кstyleOptions()
после вызова.init()
Добавьте фигуры на карту
Как и в предыдущих шагах, фигуры можно добавлять непосредственно в замыкание Map
. Создайте свою Map
внутри VStack
.
...
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
предоставляет объект PlaceTapInfo
, из которого можно получить идентификатор места, на которое нажали маркер.
Вы можете использовать идентификатор места для поиска дополнительных сведений с помощью Places SDK или Places API .
Добавить новый Swift View
Добавьте следующий код в новый файл Swift с именем PlaceTapDemo.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. (Необязательно) Продолжайте дальше
Расширенные возможности анимации камеры
В некоторых случаях требуется плавная анимация последовательности или списка локаций или состояний камеры, например, в авиасимуляторе или при воспроизведении похода или бега.
На этом этапе вы узнаете, как загрузить список локаций из файла и последовательно выполнить анимацию каждой локации.
Загрузите файл, содержащий последовательность местоположений.
Загрузите flightpath.json
из репозитория примеров приложений GitHub .
Создайте новую папку в вашем проекте Xcode с именем JSON
.
Перетащите flightpath.json
в папку JSON
в Xcode.
Установите цель как основную цель вашего приложения. Проверьте, что настройки Copy Bundle Resources вашего проекта включают этот файл.
Создайте в своем приложении два новых файла Swift с именами FlightPathData.swift
и FlightDataLoader.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()
приведет к тому, что вид будет «прыгать» между каждым местоположением.
Сначала объявите камеру с именем «innsbruck» в MapHelpers.swift
.
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
. Добавьте Map
и Button
внутри VStack
. Настройте Button
для переключения состояния булевой переменной animation
.
Объявите объект State
для FlightDataLoader
, который загрузит файл 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
с состоянием Camera. Функция 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
)
}
Соберите анимацию
Вызовите keyframeAnimator
после инициализации Map
и задайте начальные значения.
Вам понадобится начальное состояние камеры, основанное на первой точке траектории полета.
Анимация должна запускаться на основе изменения состояния переменной.
Содержимое 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
с пунктом назначения FlightPathDemo()
в GoogleMaps3DDemoApp.swift
и запустите приложение, чтобы опробовать его.
11. Поздравления
Вы успешно создали приложение, которое
- Добавляет в ваше приложение базовую 3D-карту.
- Добавляет на карту маркеры, линии, полигоны и модели.
- Реализует код для управления камерой для полета по карте и вокруг определенных мест.
Что вы узнали
- Как добавить пакет
GoogleMaps3D
в приложение Xcode SwiftUI. - Как инициализировать 3D-карту с помощью API-ключа и вида по умолчанию.
- Как добавлять на карту маркеры, 3D-модели, линии и полигоны.
- Как управлять камерой для анимации перемещения в другое место.
- Как обрабатывать события нажатия на маркеры мест.
Что дальше?
- Более подробную информацию о возможностях Maps 3D SDK для iOS можно найти в руководстве разработчика .
- Помогите нам создать контент, который вы найдете наиболее полезным, ответив на следующий опрос: