Создайте свою первую 3D-карту с помощью SwiftUI

1. Прежде чем начать

В этой лабораторной работе вы узнаете, как создать приложение 3D-карт в SwiftUI с использованием Maps 3D SDK для iOS.

Приложение, показывающее 3D-карту Сан-Франциско

Вы узнаете:

  • Как управлять камерой, чтобы просматривать локации и летать по карте.
  • Как добавлять маркеры и модели
  • Как рисовать линии и многоугольники
  • Как обрабатывать нажатия пользователей на маркеры мест.

Предпосылки

  • Проект 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 Карт», чтобы создать учетную запись для выставления счетов и проект.

  1. В Cloud Console щелкните раскрывающееся меню проектов и выберите проект, который вы хотите использовать для этой кодовой лаборатории.

  1. Включите API и SDK Google Maps Platform, необходимые для этой кодовой лаборатории в Google Cloud Marketplace . Для этого выполните действия, описанные в этом видео или в этой документации .
  2. Сгенерируйте ключ API на странице Credentials в Cloud Console. Вы можете следовать инструкциям в этом видео или в этой документации . Для всех запросов к Google Maps Platform требуется ключ API.

Включить Maps 3D SDK для iOS

Maps 3D SDK для iOS можно найти, воспользовавшись ссылкой в ​​меню «Платформа Google Карт» > «API и службы» в консоли.

Нажмите «Включить», чтобы включить API в выбранном проекте.

Включите Maps 3D SDK в Google Console

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
    }
  }
}

Соберите и запустите свое приложение, чтобы проверить, правильно ли оно загружается. Вы должны увидеть карту земного шара.

3D-карта, показывающая Землю

4. Используйте камеру для управления видом карты

Создать объект состояния камеры

Виды 3D-карт контролируются классом Camera . На этом этапе вы научитесь указывать местоположение, высоту, направление, наклон, крен и диапазон для настройки вида карты.

3D-карта Сан-Франциско

Создайте класс 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. Базовая анимация камеры

До сих пор вы использовали камеру для указания одного местоположения с наклоном, высотой, направлением и диапазоном. На этом этапе вы узнаете, как перемещать вид камеры, анимируя эти свойства из начального состояния в новое состояние.

3D-карта Сиэтла

Летать в место

Вы будете использовать метод 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 будет использовать значок по умолчанию для маркера. Наконец, вы настроите высоту маркера и другие свойства, чтобы изменить способ его отображения.

3D-карта с маркером на карте

Создайте новое представление 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 для этой кодовой лаборатории.

3D-карта Сан-Франциско с моделью воздушного шара

Добавьте файл модели в свой проект

Создайте новую папку в вашем проекте 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 или базы данных.

3D-карта Сан-Франциско, на которой изображены два полигона и полилиния

Создайте несколько объектов-фигур для управления данными фигур.

Добавьте новое определение 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. (Необязательно) Продолжайте дальше

Расширенные возможности анимации камеры

В некоторых случаях требуется плавная анимация последовательности или списка локаций или состояний камеры, например, в авиасимуляторе или при воспроизведении похода или бега.

На этом этапе вы узнаете, как загрузить список локаций из файла и последовательно выполнить анимацию каждой локации.

3D-карта подъезда к Инсбруку

Загрузите файл, содержащий последовательность местоположений.

Загрузите 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 можно найти в руководстве разработчика .
  • Помогите нам создать контент, который вы найдете наиболее полезным, ответив на следующий опрос:

Какие еще лабораторные работы вы хотели бы увидеть?

Визуализация данных на картах Подробнее о настройке стиля моих карт Создание 3D-взаимодействий на картах