Tworzenie pierwszej mapy 3D za pomocą SwiftUI

1. Zanim zaczniesz

Z tego ćwiczenia w Codelab dowiesz się, jak utworzyć aplikację z mapami 3D w SwiftUI za pomocą pakietu SDK Map 3D na iOS.

Aplikacja z mapą 3D San Francisco

Dowiesz się:

  • Jak sterować kamerą, aby wyświetlać lokalizacje i przemieszczać się po mapie.
  • Dodawanie znaczników i modeli
  • Jak rysować linie i wielokąty
  • Jak obsługiwać kliknięcia przez użytkowników znaczników miejsc.

Wymagania wstępne

  • projekt w Konsoli Google z włączonymi płatnościami;
  • Klucz interfejsu API, opcjonalnie ograniczony do pakietu SDK Map 3D na iOS.
  • podstawowa znajomość tworzenia aplikacji na iOS za pomocą SwiftUI;

Jakie zadania wykonasz

  • Konfigurowanie Xcode i wprowadzanie pakietu SDK za pomocą menedżera pakietów Swift
  • Konfigurowanie aplikacji do korzystania z klucza interfejsu API
  • Dodawanie do aplikacji podstawowej mapy 3D
  • Sterowanie kamerą w celu przemieszczania się do określonych miejsc i poruszania się wokół nich
  • Dodawanie znaczników, linii, wielokątów i modeli do mapy

Czego potrzebujesz

  • Xcode 15 lub nowsza wersja.

2. Konfiguracja

Aby wykonać następny krok, musisz włączyć pakiet SDK Map 3D na iOS.

Konfigurowanie Google Maps Platform

Jeśli nie masz jeszcze konta Google Cloud Platform i utworzonego projektu z włączonymi płatnościami, zapoznaj się z artykułem Pierwsze kroki z Google Maps Platform, aby utworzyć konto rozliczeniowe i projekt.

  1. W konsoli Google Cloud kliknij menu projektu i wybierz projekt, którego chcesz użyć w tym CodeLab.

  1. Włącz interfejsy API i pakiety SDK Google Maps Platform wymagane w tym laboratorium kodu na platformie Google Cloud Marketplace. Aby to zrobić, wykonaj czynności opisane w tym filmie lub w tej dokumentacji.
  2. Wygeneruj klucz interfejsu API na stronie Dane logowania w konsoli Cloud. Możesz wykonać czynności opisane w tym filmie lub w tej dokumentacji. Wszystkie żądania do Google Maps Platform wymagają klucza interfejsu API.

Włączanie pakietu SDK Map 3D na iOS

Pakiet SDK Map 3D na iOS znajdziesz w konsoli, klikając w menu Google Maps Platform > Interfejsy API i usługi.

Kliknij Włącz, aby włączyć interfejs API w wybranym projekcie.

Włączanie pakietu SDK Map 3D w Konsoli Google

3. Tworzenie podstawowej aplikacji SwiftUI

Uwaga: kod rozwiązania dla każdego kroku znajdziesz w repozytorium przykładowej aplikacji na GitHubie .

Utwórz nową aplikację w Xcode.

Kod potrzebny do wykonania tego kroku znajdziesz w folderze GoogleMaps3DDemo na GitHubie.

Otwórz Xcode i utwórz nową aplikację. W oknie dialogowym „Framework” wybierz SwiftUI.

Nazwij aplikację GoogleMaps3DDemo i nadaj jej nazwę pakietu com.example.GoogleMaps3DDemo.

Importowanie biblioteki GoogleMaps3D do projektu

Dodaj pakiet SDK do projektu za pomocą menedżera pakietów Swift.

W projekcie lub obszarze roboczym Xcode wybierz Plik > Dodaj zależności pakietu. Wpisz https://github.com/googlemaps/ios-maps-3d-sdk jako adres URL, naciśnij klawisz Enter, aby zaimportować pakiet, i kliknij „Dodaj pakiet”.

W oknie Wybieranie produktów pakietu sprawdź, czy GoogleMaps3D zostanie dodany do głównego celu. Gdy skończysz, kliknij Dodaj pakiet.

Aby sprawdzić instalację, otwórz panel Ogólne. W ramach opcji Ramy, Biblioteki i Zawartość osadzona powinny być widoczne zainstalowane pakiety. Możesz też wyświetlić sekcję Zależność pakietu w Nawigatorze projektu, aby sprawdzić pakiet i jego wersję.

Dodawanie klucza interfejsu API

Możesz zakodować klucz interfejsu API w aplikacji, ale nie jest to zalecana praktyka. Dodanie pliku konfiguracyjnego pozwala zachować w tajnych klucz interfejsu API i uniknąć jego sprawdzenia w systemie kontroli wersji.

Utwórz nowy plik konfiguracji w folderze głównym projektu.

W Xcode sprawdź, czy wyświetlasz okno Eksploratora projektu. Kliknij prawym przyciskiem myszy katalog główny projektu i wybierz „Nowy plik ze szablonu”. Przewiń, aż zobaczysz „Plik ustawień konfiguracji”. Wybierz tę opcję i kliknij „Dalej”. Nazwij plik Config.xcconfig i upewnij się, że wybrany jest folder główny projektu. Aby utworzyć plik, kliknij „Utwórz”.

W edytorze dodaj do pliku konfiguracji wiersz w ten sposób: MAPS_API_KEY = YOUR_API_KEY

Zastąp YOUR_API_KEY swoim kluczem API.

Dodaj to ustawienie do Info.plist.

Aby to zrobić, wybierz katalog główny projektu i kliknij kartę „Informacje”.

Dodaj nową właściwość o nazwie MAPS_API_KEY o wartości $(MAPS_API_KEY).

Przykładowy kod aplikacji zawiera plik Info.plist, który określa tę właściwość.

Dodawanie mapy

Otwórz plik o nazwie GoogleMaps3DDemoApp.swift. Jest to punkt wejścia i główna nawigacja w aplikacji.

Wywołuje on funkcję ContentView(), która wyświetla komunikat „Hello World”.

Otwórz plik ContentView.swift w edytorze.

Dodaj oświadczenie import dla GoogleMaps3D.

Usuń kod w bloku kodu var body: some View {}. Zadeklaruj nową zmienną Map() w bloku body.

Minimalna konfiguracja potrzebna do zainicjowania Map to MapMode. Ma on 2 możliwe wartości:

  • .hybrid – zdjęcia satelitarne z drożami i etykietami lub
  • .satellite – tylko zdjęcia satelitarne.

Wybierz firmę .hybrid.

Plik ContentView.swift powinien wyglądać tak.

import GoogleMaps3D
import SwiftUI

@main
struct ContentView: View {
    var body: some View {
      Map(mode: .hybrid)
    }
}

Ustaw klucz interfejsu API.

Klucz API musi zostać ustawiony przed zainicjowaniem mapy.

Aby to zrobić, ustaw Map.apiKey w obiekcie init() w obiekcie View, który zawiera mapę. Możesz też ustawić go w GoogleMaps3DDemoApp.swift przed wywołaniem funkcji ContentView().

GoogleMaps3DDemoApp.swift ustaw wartość Map.apiKey w module obsługi zdarzenia onAppear w komponencie WindowGroup.

Pobieranie klucza interfejsu API z pliku konfiguracyjnego

Użyj polecenia Bundle.main.infoDictionary, aby uzyskać dostęp do ustawienia MAPS_API_KEY utworzonego w pliku konfiguracji.

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

Utwórz i uruchom aplikację, aby sprawdzić, czy wczytuje się prawidłowo. Powinna wyświetlić się mapa kuli ziemskiej.

Mapa 3D pokazująca Ziemię

4. Sterowanie widokiem mapy za pomocą aparatu

Tworzenie obiektu stanu aparatu

Wyświetlanie mapy 3D jest kontrolowane przez klasę Camera. W tym kroku dowiesz się, jak określić lokalizację, wysokość, kierunek, pochylenie, obrót i zakres, aby dostosować widok mapy.

Widok mapy 3D San Francisco

Utwórz klasę pomocniczą do przechowywania ustawień aparatu

Dodaj nowy pusty plik o nazwie MapHelpers.swift. W nowym pliku zaimportuj GoogleMaps3D i dodaj rozszerzenie do klasy Camera. Dodaj zmienną o nazwie sanFrancisco. Inicjuje tę zmienną jako nowy obiekt Camera. Znajdź kamerę w miejscu latitude: 37.39, longitude: -122.08.

import GoogleMaps3D

extension Camera {
 public static var sanFrancisco: Camera = .init(latitude: 37.39, longitude: -122.08)
}

Dodawanie nowego widoku do aplikacji

Utwórz nowy plik o nazwie CameraDemo.swift. Dodaj do pliku podstawowy zarys nowego widoku SwiftUI.

Dodaj zmienną @State o nazwie camera i typie Camera. Zainicjuj go w zaraz zdefiniowanym aparacie sanFrancisco.

Korzystając z funkcji @State, możesz powiązać Mapę ze stanem kamery i używać jej jako źródła informacji.

@State var camera: Camera = .sanFrancisco

Zmień wywołanie funkcji Map(), aby uwzględnić właściwość camera. Użyj powiązania stanu kamery $camera, aby zainicjować właściwość camera w obiekcie 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)
    }
  }
}

Dodawanie podstawowego interfejsu nawigacji do aplikacji

Dodaj NavigationView do głównego punktu wejścia aplikacji, GoogleMaps3DDemoApp.swift.

Dzięki temu użytkownicy zobaczą listę wersji demonstracyjnych i będą mogli kliknąć każdą z nich, aby ją otworzyć.

Kliknij Edytuj GoogleMaps3DDemoApp.swift, aby dodać nowy NavigationView.

Dodaj element List zawierający 2 deklaracje NavigationLink.

Pierwszy NavigationLink powinien otwierać ContentView()Text opisem Basic Map.

Drugi NavigationLink powinien otworzyć CameraDemo().

...
      NavigationView {
        List {
          NavigationLink(destination: ContentView()) {
            Text("Basic Map")
          }
          NavigationLink(destination: CameraDemo()) {
            Text("Camera Demo")
          }
        }
      }
...

Dodawanie podglądu Xcode

Podglądy to zaawansowana funkcja Xcode, która umożliwia wyświetlanie aplikacji i działanie w niej podczas wprowadzania zmian.

Aby dodać podgląd, otwórz CameraDemo.swift. Dodaj blok kodu #Preview {} poza elementem struct.

#Preview {
 CameraDemo()
}

Otwórz lub odśwież panel Podgląd w Xcode. Na mapie powinno być zaznaczone San Francisco.

Konfigurowanie widoków 3D niestandardowych

Możesz podać dodatkowe parametry, aby sterować kamerą:

  • heading: kierunek w stopniach od północy, w którym ma być skierowana kamera.
  • tilt: kąt pochylenia w stopniach, gdzie 0 to bezpośrednio nad głową, a 90 to patrzący poziomo.
  • roll: kąt obrotu wokół osi pionowej kamery wyrażony w stopniach
  • range: odległość w metrach od kamery do lokalizacji z szerokością i długością geograficzną
  • altitude: wysokość aparatu nad poziomem morza

Jeśli nie podasz żadnego z tych dodatkowych parametrów, zostaną użyte wartości domyślne.

Aby widok z kamery pokazywał więcej danych 3D, ustaw początkowe parametry w taki sposób, aby wyświetlać bliższy, pochylony widok.

Zmień definicję typu Camera w definicji MapHelpers.swift, aby uwzględnić wartości altitude, heading, tilt, rollrange.

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)

Zbuduj i uruchom aplikację, aby zobaczyć i przetestować nowy widok 3D.

5. Podstawowe animacje aparatu

Do tej pory używasz aparatu, aby określić pojedynczą lokalizację z pochyleniem, wysokością, kierunkiem i zakresem. W tym kroku dowiesz się, jak przesuwać widok kamery, animując te właściwości od stanu początkowego do nowego.

Mapa 3D Seattle

Poleć do lokalizacji

Aby animować kamerę od początkowej do nowej lokalizacji, użyjesz metody Map.flyCameraTo().

Metoda flyCameraTo() przyjmuje kilka parametrów:

  • Camera reprezentujący lokalizację końcową.
  • duration: czas trwania animacji w sekundach.
  • trigger: obserwowalny obiekt, który uruchamia animację po zmianie stanu.
  • completion: kod, który zostanie wykonany po zakończeniu animacji.

Określ miejsce docelowe

Otwórz plik MapHelpers.swift.

Zdefiniuj nowy obiekt kamery, aby pokazać Seattle.

public static var seattle: Camera = .init(latitude:
47.6210296,longitude: -122.3496903, heading: 149.0, tilt: 77.0, roll: 0.0, range: 4000)

Dodaj przycisk, który uruchamia animację.

Otwórz CameraDemo.swift. Zadeklaruj nową zmienną logiczną wewnątrz struct.

Nazwij ją animate i nadaj jej wartość początkową false.

@State private var animate: Bool = false

Dodaj Button poniżej VStack. Button inicjuje animację mapy.

Nadaj Button odpowiednią Text, np. „Start Flying” (Rozpocznij lot).

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") {
      }
    }
  }
}

W bloku Button closure dodaj kod, aby przełączyć stan zmiennej animate.

      Button("Start Flying") {
        animate.toggle()
      }

Uruchom animację.

Dodaj kod, który uruchamia animację flyCameraTo(), gdy zmienia się stan zmiennej animate.

  var body: some View {
    VStack{
      Map(camera: $camera, mode: .hybrid)
        .flyCameraTo(
          .seattle,
          duration: 5,
          trigger: animate,
          completion: {  }
        )
      Button("Start Flying") {
        animate.toggle()
      }
    }
  }

Oblot wokół lokalizacji

Oblot wokół lokalizacji można wykonać za pomocą metody Map.flyCameraAround(). Ta metoda przyjmuje kilka parametrów:

  • Camera określający lokalizację i widok.
  • duration w sekundach.
  • rounds: liczba powtórzeń animacji.
  • trigger: obserwowany obiekt, który uruchamia animację.
  • callback: kod, który zostanie wykonany podczas odtwarzania animacji.

Zdefiniuj nową zmienną @State o nazwie flyAround z wartością początkową false.

Następnie dodaj wywołanie metody flyCameraAround() bezpośrednio po wywołaniu metody flyCameraTo().

Czas trwania przelotu powinien być stosunkowo długi, aby widok zmieniał się płynnie.

Pamiętaj, aby po zakończeniu działania flyCameraTo() wywołać animację flyCameraAround(), zmieniając stan obiektu wyzwalacza.

Twój kod powinien wyglądać tak.

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

Wyświetl podgląd lub uruchom aplikację, aby sprawdzić, czy po zakończeniu animacji flyCameraTo() kamera przelatuje wokół miejsca docelowego.

6. Dodaj znacznik do mapy.

W tym kroku dowiesz się, jak narysować znacznik na mapie.

Utwórz obiekt Marker i dodaj go do mapy. Pakiet SDK użyje domyślnej ikony znacznika. Na koniec dostosuj wysokość znacznika i inne właściwości, aby zmienić sposób jego wyświetlania.

Mapa 3D ze znacznikiem

Utwórz nowy widok SwiftUI na potrzeby demonstracji wskaźnika.

Dodaj do projektu nowy plik Swift. Nazwij ją MarkerDemo.swift.

Dodaj zarys widoku SwiftUI i inicjuj mapę tak samo jak w funkcji CameraDemo.

import SwiftUI
import GoogleMaps3D

struct MarkerDemo: View {
  @State var camera: Camera = .sanFrancisco
  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid)
    }
  }
}

Inicjowanie obiektu Marker

Zadeklaruj nową zmienną znacznika o nazwie mapMarker. U góry bloku kodu structMarkerDemo.swift.

Umieść definicję w wierszu pod deklaracją camera. Ten przykładowy kod inicjalizuje wszystkie dostępne właściwości.

  @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"
  )

Dodaj znacznik do mapy.

Aby narysować znacznik, dodaj go do funkcji zamykającej wywoływanej podczas tworzenia mapy.

struct MarkerDemo: View {
  @State var camera: Camera = .sanFrancisco
  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {
        mapMarker
      }
    }
  }
}

Dodaj nowy element NavigationLink do elementu GoogleMaps3DDemoApp.swift z miejscem docelowym MarkerDemo() i elementem Text o nazwie „Marker Demo”.

...
      NavigationView {
        List {
          NavigationLink(destination: Map()) {
            Text("Basic Map")
          }
          NavigationLink(destination: CameraDemo()) {
            Text("Camera Demo")
          }
          NavigationLink(destination: MarkerDemo()) {
            Text("Marker Demo")
          }
        }
      }
...

Wyświetlanie podglądu i uruchamianie aplikacji

Odśwież podgląd lub uruchom aplikację, aby zobaczyć znacznik.

Znaczniki wytłaczane

Za pomocą właściwości altitude i altitudeMode możesz umieszczać znaczniki nad ziemią lub siatką 3D.

Skopiuj deklarację mapMarker w bloku MarkerDemo.swift do nowej zmiennej Marker o nazwie extrudedMarker.

Ustaw wartość niezerową dla parametru altitude, np. 50.

Zmień wartość altitudeMode na .relativeToMesh, a wartość extruded na true. Aby umieścić znacznik na szczycie wieżowca, użyj w tym fragmencie kodu wartości latitudelongitude.

  @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"
  )

Uruchom ponownie aplikację lub wyświetl jej podgląd. znacznik powinien pojawiać się na szczycie budynku 3D;

7. Dodaj model do mapy.

Pole Model można dodawać w taki sam sposób jak pole Marker. Potrzebujesz pliku modelu, do którego można uzyskać dostęp za pomocą adresu URL lub dodać go jako plik lokalny w projekcie. W tym kroku użyjemy pliku lokalnego, który możesz pobrać z repozytorium GitHub dla tego Codelab.

Mapa 3D San Francisco z modelem balonu na ogrzane powietrze

Dodawanie pliku modelu do projektu

Utwórz w projekcie Xcode nowy folder o nazwie Models.

Pobierz model z repozytorium przykładowej aplikacji w GitHub. Dodaj go do projektu, przeciągając go do nowego folderu w widoku projektu Xcode.

Upewnij się, że ustawiony przez Ciebie obiekt jest głównym celem dla Twojej aplikacji.

Sprawdź ustawienia w sekcji Etapy kompilacji > Kopiowanie zasobów pakietu w projekcie. Plik modelu powinien znajdować się na liście zasobów skopiowanych do pakietu. Jeśli go nie ma, kliknij „+”, aby go dodać.

Dodaj model do aplikacji.

Utwórz nowy plik SwiftUI o nazwie ModelDemo.swift.

Dodaj instrukcje import dla zmiennych SwiftUIGoogleMaps3D tak jak w poprzednich krokach.

Zadeklaruj Map w ramach VStack w Twoim body.

import SwiftUI
import GoogleMaps3D

struct ModelDemo: View {
  @State var camera: Camera = .sanFrancisco
  
  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {
        
      }
    }
  }
}

Pobierz ścieżkę modelu z pakietu. Dodaj kod dla tego poza struct.

private let fileUrl = Bundle.main.url(forResource: "balloon", withExtension: "glb")

Zadeklaruj zmienną dla modelu wewnątrz struktury.

Podaj wartość domyślną na wypadek, gdyby nie podano wartości 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. Użyj modelu w swojej Mapie.

Podobnie jak w przypadku dodawania Marker, w deklaracji Map podaj tylko odniesienie do Model.

  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {
        balloonModel
      }
    }
  }

Wyświetlanie podglądu i uruchamianie aplikacji

Dodaj nowy element NavigationLink do elementu GoogleMaps3DDemoApp.swift, ustawiając jako miejsce docelowe element ModelDemo() i element Text „Model Demo”.

...
          NavigationLink(destination: ModelDemo()) {
            Text("Model Demo")
          }
...

Aby zobaczyć model, odśwież podgląd lub uruchom aplikację.

8. Narysuj linię i wielokąt na mapie.

W tym kroku dowiesz się, jak dodawać linie i kształty wielokąta do mapy 3D.

Dla uproszczenia kształty zostaną zdefiniowane jako tablice obiektów LatLngAltitude. W rzeczywistej aplikacji dane mogą być ładowane z pliku, wywołania interfejsu API lub bazy danych.

Mapa 3D San Francisco z 2 wielokątami i 1 linią łamaną

Utwórz obiekty kształtów, aby zarządzać danymi kształtów.

Dodaj do MapHelpers.swift nową definicję Camera, która obejmuje centrum San Francisco.

  public static var downtownSanFrancisco: Camera = .init(latitude: 37.7905, longitude: -122.3989, heading: 25, tilt: 71, range: 2500) 

Dodaj do projektu nowy plik o nazwie ShapesDemo.swift. Dodaj struct o nazwie ShapesDemo, który implementuje protokół View, i dodaj do niego body.

struct ShapesDemo: View {
  @State var camera: Camera = .downtownSanFrancisco

  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {

      }
    }
  }
}

Klasy, których użyjesz do zarządzania danymi kształtów, to PolylinePolygon. Otwórz ShapesDemo.swift i dodaj je do struct w ten sposób:

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

Zwróć uwagę na użyte parametry inicjowania.

  • Wartość altitudeMode: .relativeToGround służy do wytłaczania wielokątów do określonej wysokości nad ziemią.
  • altitudeMode: .clampToGround służy do dostosowania wielokąta do kształtu powierzchni Ziemi.
  • style są ustawiane w obiektach Polygon przez złączenie wywołania metody styleOptions() z wywołaniem metody .init().

Dodawanie kształtów do mapy

Podobnie jak w poprzednich krokach kształty można dodawać bezpośrednio do zamknięcia Map. Utwórz Map w VStack.

...
  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {
        polyline
        originPolygon
        destinationPolygon
      }
    }
  }
...

Wyświetlanie podglądu i uruchamianie aplikacji

Dodaj kod podglądu i sprawdź aplikację w oknie Podgląd w Xcode.

#Preview {
  ShapesDemo()
}

Aby uruchomić aplikację, dodaj do GoogleMaps3DDemoApp.swift nowy element NavigationLink, który otworzy nowy widok demonstracyjny.

...
          NavigationLink(destination: ShapesDemo()) {
            Text("Shapes Demo")
          }
...

Uruchom aplikację i sprawdź dodane kształty.

9. Obsługa zdarzeń dotknięcia znaczników miejsca

Na tym etapie dowiesz się, jak reagować na kliknięcia przez użytkownika znaczników miejsc.

Mapa z wyskakującym okienkiem z identyfikatorem miejsca

Uwaga: aby zobaczyć na mapie znaczniki miejsc, musisz ustawić wartość parametru MapMode na .hybrid.

Obsługa kliknięcia wymaga implementacji metody Map.onPlaceTap.

Zdarzenie onPlaceTap udostępnia obiekt PlaceTapInfo, z którego można uzyskać identyfikator miejsca docelowego dotkniętego znacznika miejsca.

Za pomocą identyfikatora miejsca możesz uzyskać więcej informacji, korzystając z pakietu SDK Miejsc lub interfejsu API Miejsc.

Dodawanie nowego widoku Swift

Dodaj ten kod do nowego pliku Swift o nazwie 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()
}

Wyświetlanie podglądu i uruchamianie aplikacji

Otwórz panel Podgląd, aby wyświetlić podgląd aplikacji.

Aby uruchomić aplikację, dodaj nowy element NavigationLink do elementu GoogleMaps3DDemoApp.swift.

...
          NavigationLink(destination: PlaceTapDemo()) {
            Text("Place Tap Demo")
          }
...

10. (Opcjonalnie) Przejdź dalej

Zaawansowane animacje aparatu

Niektóre przypadki użycia wymagają płynnej animacji w ramach sekwencji lub listy lokalizacji bądź stanów kamery, np. w symulatorze lotu lub podczas odtwarzania wędrówki czy biegu.

W tym kroku dowiesz się, jak wczytać listę lokalizacji z pliku i animować każdą z nich po kolei.

Widok mapy 3D zbliżający się do Innsbrucka

Załaduj plik zawierający sekwencję lokalizacji.

Pobierz flightpath.json z repozytorium przykładowej aplikacji na GitHubie.

Utwórz w projekcie Xcode nowy folder o nazwie JSON.

Przeciągnij flightpath.json do folderu JSON w Xcode.

Ustaw go jako główny cel aplikacji. Sprawdź, czy ustawienia kopiowania zasobów pakietu w projekcie zawierają ten plik.

Utwórz w aplikacji 2 nowe pliki Swift o nazwach FlightPathData.swiftFlightDataLoader.swift.

Skopiuj do aplikacji ten kod. Tworzy on struktury i klasy, które odczytują lokalny plik o nazwie „flighpath.json” i dekodują go jako JSON.

Struktury FlightPathDataFlightPathLocation reprezentują strukturę danych w pliku JSON jako obiekty Swift.

Klasa FlightDataLoader odczytuje dane z pliku i je zdekoduje. Używa on protokołu ObservableObject, aby umożliwić aplikacji obserwowanie zmian w jej danych.

Przetworzone dane są udostępniane za pomocą opublikowanej usługi.

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.")
        }
      }
    }
  }
}

Animowanie kamery w każdej lokalizacji

Aby animować kamerę między sekwencjami kroków, użyj KeyframeAnimator.

Każdy Keyframe zostanie utworzony jako CubicKeyframe, dzięki czemu zmiany stanu kamery będą płynnie animowane.

Użycie flyCameraTo() spowoduje „przeskakiwanie” między poszczególnymi lokalizacjami.

Najpierw zadeklaruj kamerę o nazwie „innsbruck” w 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)

Teraz skonfiguruj nowy widok danych w nowym pliku o nazwie FlyAlongRoute.swift.

Zaimportuj SwiftUIGoogleMaps3D. Dodaj Map i Button wewnątrz VStack. Ustaw zmienną Button, aby przełączyć stan zmiennej logicznej animation.

Zadeklaruj obiekt State dla FlightDataLoader, który wczyta plik JSON po jego zainicjowaniu.

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

Tworzenie klatek kluczowych

Podstawowym procesem jest utworzenie funkcji, która zwraca nowy kadr w sekwencji animacji. Każdy nowy kadr określa następny stan kamery, który animator ma animować. Po utworzeniu tej funkcji wywołuj ją z poszczególnymi lokalizacjami z pliku w kolejności.

Dodaj do struktury FlyAlongRoute 2 funkcje. Funkcja makeKeyFrame zwraca CubicKeyframe z stanem aparatu. Funkcja makeCamera wykonuje krok w sekwencji danych lotu i zwraca obiekt Camera reprezentujący ten krok.

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

Łączenie animacji

Wywołaj funkcję keyframeAnimator po zainicjowaniu funkcji Map i ustaw wartości początkowe.

Potrzebujesz początkowego stanu kamery na podstawie pierwszej lokalizacji na ścieżce lotu.

Animacja powinna być uruchamiana na podstawie zmiany stanu zmiennej.

Treści keyframeAnimator powinny być mapą.

Rzeczywista lista klatek kluczowych jest generowana przez przechodzenie przez wszystkie lokalizacje na ścieżce lotu.

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

Wyświetl podgląd aplikacji i uruchom ją.

Aby wyświetlić podgląd widoku, otwórz panel Podgląd.

Dodaj nowy NavigationLink z miejscem docelowym FlightPathDemo() do GoogleMaps3DDemoApp.swift i uruchom aplikację, aby ją wypróbować.

11. Gratulacje

Udało Ci się utworzyć aplikację, która

  • Dodaje do aplikacji podstawową mapę 3D.
  • Dodaje do mapy znaczniki, linie, wielokąty i modele.
  • Wdraża kod, który umożliwia sterowanie kamerą i przemieszczanie się po mapie oraz po określonych lokalizacjach.

Czego się nauczyłeś

  • Jak dodać pakiet GoogleMaps3D do aplikacji SwiftUI w Xcode.
  • Jak zainicjować mapę 3D za pomocą klucza interfejsu API i widoku domyślnego
  • Jak dodawać do mapy znaczniki, modele 3D, linie i wielokąty
  • Jak sterować kamerą, aby animować ruch do innej lokalizacji.
  • Jak obsługiwać zdarzenia kliknięcia na znacznikach miejsca.

Co dalej?

  • Więcej informacji o tym, co możesz robić za pomocą pakietu SDK Map 3D na iOS, znajdziesz w przewodniku dla deweloperów.
  • Pomóż nam tworzyć treści, które będą dla Ciebie najbardziej przydatne, wypełniając tę ankietę:

Jakie jeszcze laboratoria kodu chciałbyś/chcesz zobaczyć?

Wizualizacja danych na mapach Więcej informacji o dostosowywaniu stylu map Tworzenie interakcji 3D na mapach