Crea la tua prima mappa 3D con SwiftUI

1. Prima di iniziare

Questo codelab ti insegna a creare un'app di mappe 3D in SwiftUI utilizzando l'SDK Maps 3D per iOS.

App che mostra una mappa 3D di San Francisco

Scoprirai:

  • Come controllare la fotocamera per visualizzare le posizioni e volare sulla mappa.
  • Come aggiungere indicatori e modelli
  • Come disegnare linee e poligoni
  • Come gestire i clic degli utenti sugli indicatori di luogo.

Prerequisiti

  • Un progetto della Console Google con la fatturazione abilitata
  • Una chiave API, eventualmente limitata all'SDK Maps 3D per iOS.
  • Conoscenza di base dello sviluppo per iOS con SwiftUI.

In questo lab proverai a:

  • Configura Xcode e importa l'SDK utilizzando Swift Package Manager
  • Configurare l'app per utilizzare una chiave API
  • Aggiungere una mappa 3D di base all'app
  • Controllare la videocamera per volare verso e intorno a posizioni specifiche
  • Aggiungere indicatori, linee, poligoni e modelli alla mappa

Che cosa ti serve

  • Xcode 15 o versioni successive.

2. Configurazione

Per il passaggio di attivazione successivo, dovrai abilitare l'SDK Maps 3D per iOS.

Configurare Google Maps Platform

Se non hai ancora un account Google Cloud e un progetto con la fatturazione abilitata, consulta la guida Introduzione a Google Maps Platform per creare un account di fatturazione e un progetto.

  1. Nella console Cloud, fai clic sul menu a discesa del progetto e seleziona il progetto che vuoi utilizzare per questo codelab.

  1. Abilita le API e gli SDK di Google Maps Platform richiesti per questo codelab nel Google Cloud Marketplace. Per farlo, segui i passaggi descritti in questo video o in questa documentazione.
  2. Genera una chiave API nella pagina Credenziali di Cloud Console. Puoi seguire la procedura descritta in questo video o in questa documentazione. Tutte le richieste a Google Maps Platform richiedono una chiave API.

Attivare l'SDK Maps 3D per iOS

Puoi trovare l'SDK Maps 3D per iOS utilizzando il link del menu Google Maps Platform > API e servizi nella console.

Fai clic su Abilita per attivare l'API nel progetto selezionato.

Attivare l'SDK Maps 3D nella console Google

3. Creare un'app SwiftUI di base

Nota: puoi trovare il codice della soluzione per ogni passaggio nel repository dell'app di esempio di codelab su GitHub .

Crea una nuova app in Xcode.

Il codice per questo passaggio è disponibile nella cartella GoogleMaps3DDemo su GitHub.

Apri Xcode e crea una nuova app. Specifica SwiftUI.

Chiama la tua app GoogleMaps3DDemo con un nome del pacchetto com.example.GoogleMaps3DDemo.

Importa la libreria GoogleMaps3D nel progetto

Aggiungi l'SDK al progetto utilizzando Swift Package Manager.

Nel progetto o nello spazio di lavoro Xcode, vai a File > Aggiungi dipendenze del pacchetto. Inserisci https://github.com/googlemaps/ios-maps-3d-sdk come URL, premi Invio per importare il pacchetto e fai clic su "Aggiungi pacchetto".

Nella finestra Scegli i prodotti del pacchetto, verifica che GoogleMaps3D venga aggiunto al target principale designato. Al termine, fai clic su Aggiungi pacchetto.

Per verificare l'installazione, vai al riquadro Generale del target. In Frameworks, librerie e contenuti incorporati, dovresti vedere i pacchetti installati. Puoi anche visualizzare la sezione Dipendenze del pacchetto di Project Navigator per verificare il pacchetto e la relativa versione.

Aggiungi la tua chiave API

Puoi codificare la chiave API nell'app, ma questa non è una buona prassi. L'aggiunta di un file di configurazione ti consente di mantenere segreta la chiave API ed evita di inserirla nel controllo del codice sorgente.

Crea un nuovo file di configurazione nella cartella principale del progetto

In Xcode, assicurati di visualizzare la finestra Esplora progetti. Fai clic con il tasto destro del mouse sulla directory principale del progetto e seleziona "Nuovo file da modello". Scorri fino a visualizzare "File delle impostazioni di configurazione". Seleziona questa opzione e fai clic su "Avanti". Assegna al file il nome Config.xcconfig e assicurati che sia selezionata la cartella principale del progetto. Fai clic su "Crea" per creare il file.

Nell'editor, aggiungi una riga al file di configurazione come segue: MAPS_API_KEY = YOUR_API_KEY

Sostituisci YOUR_API_KEY con la tua chiave API.

Aggiungi questa impostazione a Info.plist.

Per farlo, seleziona la directory principale del progetto e fai clic sulla scheda "Informazioni".

Aggiungi una nuova proprietà denominata MAPS_API_KEY con un valore $(MAPS_API_KEY).

Il codice dell'app di esempio contiene un file Info.plist che specifica questa proprietà.

Aggiungere una mappa

Apri il file denominato GoogleMaps3DDemoApp.swift. Si tratta del punto di ingresso e della navigazione principale della tua app.

Chiama ContentView(), che mostra un messaggio Hello World.

Apri ContentView.swift nell'editor.

Aggiungi un'istruzione import per GoogleMaps3D.

Elimina il codice all'interno del blocco di codice var body: some View {}. Dichiara un nuovo Map() all'interno di body.

La configurazione minima necessaria per inizializzare un Map è un MapMode. Questo valore può assumere due valori:

  • .hybrid - immagini satellitari con strade ed etichette oppure
  • .satellite: solo immagini satellitari.

Scegli .hybrid.

Il file ContentView.swift dovrebbe avere il seguente aspetto.

import GoogleMaps3D
import SwiftUI

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

Imposta la chiave API.

La chiave API deve essere impostata prima dell'inizializzazione della mappa.

Per farlo, imposta Map.apiKey nel gestore eventi init() di qualsiasi View contenente una mappa. Puoi anche impostarlo in GoogleMaps3DDemoApp.swift prima che chiami ContentView().

In GoogleMaps3DDemoApp.swift, imposta Map.apiKey nel gestore di eventi onAppear di WindowGroup.

Recupera la chiave API dal file di configurazione

Utilizza Bundle.main.infoDictionary per accedere all'impostazione MAPS_API_KEY che hai creato nel file di configurazione.

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

Crea ed esegui l'app per verificare che venga caricata correttamente. Dovresti vedere una mappa del globo.

Mappa 3D che mostra la Terra

4. Usare una videocamera per controllare la visualizzazione mappa

Creare un oggetto dello stato della videocamera

Le visualizzazioni mappa 3D sono controllate dalla classe Camera. In questo passaggio imparerai a specificare la posizione, l'altitudine, la direzione, l'inclinazione, il roll e l'intervallo per personalizzare la visualizzazione mappa.

Vista della mappa 3D di San Francisco

Crea una classe Helpers per memorizzare le impostazioni della videocamera

Aggiungi un nuovo file vuoto denominato MapHelpers.swift. Nel nuovo file, importa GoogleMaps3D e aggiungi un'estensione alla classe Camera. Aggiungi una variabile denominata sanFrancisco. Inizializza questa variabile come nuovo oggetto Camera. Individua la videocamera in latitude: 37.39, longitude: -122.08.

import GoogleMaps3D

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

Aggiungere una nuova visualizzazione all'app

Crea un nuovo file denominato CameraDemo.swift. Aggiungi al file la struttura di base di una nuova visualizzazione SwiftUI.

Aggiungi una variabile @State denominata camera di tipo Camera. Inizializzalo con la videocamera sanFrancisco che hai appena definito.

L'utilizzo di @State ti consente di associare la mappa allo stato della videocamera e di utilizzarla come fonte attendibile.

@State var camera: Camera = .sanFrancisco

Modifica la chiamata alla funzione Map() in modo da includere una proprietà camera. Utilizza il binding dello stato della videocamera $camera per inizializzare la proprietà camera nell'oggetto @State della videocamera (.sanFrancisco).

import SwiftUI
import GoogleMaps3D

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

Aggiungere un'interfaccia utente di navigazione di base all'app

Aggiungi un NavigationView all'entry point principale dell'app, GoogleMaps3DDemoApp.swift.

In questo modo, gli utenti potranno vedere un elenco di demo e fare clic su ciascuna per aprirla.

Modifica GoogleMaps3DDemoApp.swift per aggiungere un nuovo NavigationView.

Aggiungi un List contenente due dichiarazioni NavigationLink.

Il primo NavigationLink deve aprirsi ContentView() con una Text descrizione Basic Map.

Il secondo NavigationLink dovrebbe aprire CameraDemo().

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

Aggiungere un'anteprima Xcode

Le anteprime sono una potente funzionalità di Xcode che ti consente di visualizzare e interagire con la tua app mentre apporti modifiche.

Per aggiungere un'anteprima, apri CameraDemo.swift. Aggiungi un blocco di codice #Preview {} all'esterno di struct.

#Preview {
 CameraDemo()
}

Apri o aggiorna il riquadro di anteprima in Xcode. La mappa dovrebbe mostrare San Francisco.

Configurare le viste 3D personalizzate

Puoi specificare parametri aggiuntivi per controllare la videocamera:

  • heading: l'orientamento in gradi da nord verso cui puntare la fotocamera.
  • tilt: l'angolo di inclinazione in gradi, dove 0 è direttamente sopra la testa e 90 è orizzontale.
  • roll: l'angolo di roll attorno al piano verticale della fotocamera, in gradi
  • range: la distanza in metri della fotocamera dalla posizione di latitudine e longitudine
  • altitude: l'altezza della fotocamera sopra il livello del mare

Se non fornisci nessuno di questi parametri aggiuntivi, verranno utilizzati i valori predefiniti.

Per fare in modo che la visualizzazione della fotocamera mostri più dati 3D, imposta i parametri iniziali in modo da mostrare una visualizzazione più ravvicinata e inclinata.

Modifica Camera che hai definito in MapHelpers.swift in modo da includere i valori per altitude, heading, tilt, roll e 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)

Crea ed esegui l'app per vedere ed esplorare la nuova visualizzazione 3D.

5. Animazioni di base della fotocamera

Finora hai utilizzato la fotocamera per specificare una singola posizione con inclinazione, altitudine, rotta e raggio. In questo passaggio imparerai a spostare la visualizzazione della videocamera animando queste proprietà da uno stato iniziale a uno nuovo.

Mappa 3D di Seattle

Vola verso una località

Utilizzerai il metodo Map.flyCameraTo() per animare la fotocamera dalla posizione iniziale a una nuova posizione.

Il metodo flyCameraTo() accetta una serie di parametri:

  • un Camera che rappresenta la stazione di arrivo.
  • duration: la durata dell'animazione in secondi.
  • trigger: un oggetto osservabile che attiverà l'animazione quando cambia stato.
  • completion: il codice che verrà eseguito al termine dell'animazione.

Definisci una località di destinazione

Apri il file MapHelpers.swift.

Definisci un nuovo oggetto fotocamera per mostrare Seattle.

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

Aggiungi un pulsante per attivare l'animazione.

Apri il CameraDemo.swift. Dichiara una nuova variabile booleana all'interno di struct.

Chiamalo animate con un valore iniziale di false.

@State private var animate: Bool = false

Aggiungi un Button sotto il VStack. Button avvierà l'animazione della mappa.

Assegna al Button un Text appropriato, ad esempio "Inizia a volare".

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

Nella chiusura del pulsante, aggiungi il codice per attivare/disattivare lo stato della variabile animate.

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

Avvia l'animazione.

Aggiungi il codice per attivare l'animazione flyCameraTo() quando cambia lo stato della variabile animate.

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

Volare intorno a una località

È possibile volare intorno a una località utilizzando il metodo Map.flyCameraAround(). Questo metodo accetta diversi parametri:

  • un Camera che definisce la posizione e la visualizzazione.
  • duration in secondi.
  • rounds: il numero di volte in cui ripetere l'animazione.
  • trigger: un oggetto osservabile che attiverà l'animazione.
  • callback: il codice che verrà eseguito quando viene eseguita l'animazione.

Definisci una nuova variabile @State denominata flyAround, con un valore iniziale di false.

Al termine, aggiungi una chiamata a flyCameraAround() immediatamente dopo la chiamata al metodo flyCameraTo().

La durata del sorvolo deve essere relativamente lunga in modo che la visualizzazione cambi senza interruzioni.

Assicurati di attivare l'animazione flyCameraAround() modificando lo stato dell'oggetto trigger al termine di flyCameraTo().

Il codice dovrebbe avere il seguente aspetto.

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

Visualizza l'anteprima o esegui l'app per verificare che la videocamera voli intorno alla destinazione al termine dell'animazione flyCameraTo().

6. Aggiungi un indicatore alla mappa.

In questo passaggio imparerai a disegnare un indicatore sulla mappa.

Dovrai creare un oggetto Marker e aggiungerlo alla mappa. L'SDK utilizzerà un'icona predefinita per l'indicatore. Infine, dovrai regolare l'altitudine dell'indicatore e altre proprietà per modificarne la visualizzazione.

Mappa 3D che mostra un indicatore di mappa

Crea una nuova visualizzazione SwiftUI per la demo di Marker.

Aggiungi un nuovo file Swift al progetto. Chiamalo MarkerDemo.swift.

Aggiungi il contorno di una vista SwiftUI e inizializza la mappa come hai fatto in CameraDemo.

import SwiftUI
import GoogleMaps3D

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

Inizializzare un oggetto Marker

Dichiara una nuova variabile indicatore denominata mapMarker. Nella parte superiore del blocco di codice struct in MarkerDemo.swift.

Inserisci la definizione nella riga sotto la dichiarazione camera. Questo codice di esempio inizializza tutte le proprietà disponibili.

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

Aggiungi l'indicatore alla mappa.

Per disegnare l'indicatore, aggiungilo a una chiusura chiamata quando viene creata la mappa.

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

Aggiungi un nuovo NavigationLink a GoogleMaps3DDemoApp.swift con una destinazione MarkerDemo() e Text descrivendolo come "Marker Demo".

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

Visualizza l'anteprima ed esegui l'app

Aggiorna l'anteprima o esegui l'app per visualizzare l'indicatore.

Indicatori estrusi

I marker possono essere posizionati sopra il suolo o la mesh 3D utilizzando altitude e altitudeMode.

Copia la dichiarazione mapMarker in MarkerDemo.swift in una nuova variabile Marker denominata extrudedMarker.

Imposta un valore diverso da zero per altitude, 50 è sufficiente.

Modifica altitudeMode in .relativeToMesh e imposta extruded su true. Utilizza latitude e longitude nello snippet di codice qui per posizionare l'indicatore sulla cima di un grattacielo.

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

Esegui di nuovo l'app o visualizzane l'anteprima. L'indicatore deve essere visualizzato sopra un edificio 3D.

7. Aggiungi un modello alla mappa.

Un Model può essere aggiunto nello stesso modo di un Marker. Avrai bisogno di un file modello a cui è possibile accedere tramite URL o aggiunto come file locale nel progetto. Per questo passaggio utilizzeremo un file locale che puoi scaricare dal repository GitHub per questo codelab.

Mappa 3D di San Francisco con un modello di mongolfiera

Aggiungere un file modello al progetto

Crea una nuova cartella nel progetto Xcode denominata Models.

Scarica il modello dal repository di app di esempio di GitHub. Aggiungilo al progetto trascinandolo nella nuova cartella nella visualizzazione del progetto Xcode.

Assicurati di impostare il target come target principale per la tua app.

Controlla le impostazioni Fase di compilazione > Copia risorse del bundle per il tuo progetto. Il file del modello deve essere nell'elenco delle risorse copiate nel bundle. Se non è presente, fai clic su "+" per aggiungerlo.

Aggiungi il modello all'app.

Crea un nuovo file SwiftUI denominato ModelDemo.swift.

Aggiungi istruzioni import per SwiftUI e GoogleMaps3D come nei passaggi precedenti.

Dichiara un Map all'interno di un VStack nel tuo body.

import SwiftUI
import GoogleMaps3D

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

Recupera il percorso del modello dal tuo bundle. Aggiungi il codice per questo elemento all'esterno di struct.

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

Dichiara una variabile per il modello all'interno della struttura.

Fornisci un valore predefinito nel caso in cui fileUrl non venga fornito.

  @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. Utilizza il modello con la tua mappa.

Come per l'aggiunta di un Marker, fornisci il riferimento al tuo Model nella dichiarazione Map.

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

Visualizza l'anteprima ed esegui l'app

Aggiungi un nuovo NavigationLink a GoogleMaps3DDemoApp.swift, con destinazione ModelDemo() e Text "Model Demo".

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

Aggiorna l'anteprima o esegui l'app per visualizzare il modello.

8. Disegna una linea e un poligono sulla mappa.

In questo passaggio imparerai ad aggiungere linee e forme poligonali alla mappa 3D.

Per semplicità, definirai le forme come array di oggetti LatLngAltitude. In un'applicazione reale, i dati potrebbero essere caricati da un file, da una chiamata API o da un database.

Mappa 3D di San Francisco che mostra due poligoni e una polilinea

Crea alcuni oggetti forma per gestire i dati delle forme.

Aggiungi una nuova definizione di Camera a MapHelpers.swift che esamini il centro di San Francisco.

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

Aggiungi un nuovo file al progetto denominato ShapesDemo.swift. Aggiungi un struct denominato ShapesDemo che implementa il protocollo View e aggiungi un body.

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

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

      }
    }
  }
}

Le classi che utilizzerai per gestire i dati delle forme sono Polyline e Polygon. Apri ShapesDemo.swift e aggiungili a struct come segue.

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

Tieni presente i parametri di inizializzazione utilizzati.

  • altitudeMode: .relativeToGround viene utilizzato per estrudere i poligoni a un'altezza specifica sopra il suolo.
  • altitudeMode: .clampToGround viene utilizzato per fare in modo che il polilinea segua la forma della superficie terrestre.
  • gli stili vengono impostati sugli oggetti Polygon collegando una chiamata di metodo a styleOptions() dopo la chiamata a .init()

Aggiungi le forme alla mappa

Come nei passaggi precedenti, le forme possono essere aggiunte direttamente alla chiusura Map. Crea il Map all'interno di un VStack.

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

Visualizza l'anteprima ed esegui l'app

Aggiungi il codice di anteprima e ispeziona l'app nel riquadro Anteprima di Xcode.

#Preview {
  ShapesDemo()
}

Per eseguire l'app, aggiungi un nuovo NavigationLink a GoogleMaps3DDemoApp.swift che apra la nuova visualizzazione demo.

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

Esegui l'app ed esplora le forme che hai aggiunto.

9. Gestire gli eventi di tocco sugli indicatori di luogo

In questo passaggio imparerai a rispondere ai tocchi dell'utente sui segnaposto.

Mappa che mostra una finestra popup con un ID luogo

Nota: per visualizzare gli indicatori dei luoghi sulla mappa, devi impostare MapMode su .hybrid.

La gestione del tocco richiede l'implementazione del metodo Map.onPlaceTap.

L'evento onPlaceTap fornisce un oggetto PlaceTapInfo da cui puoi ottenere l'ID posizione dell'indicatore di luogo toccato.

Puoi utilizzare l'ID luogo per cercare ulteriori dettagli utilizzando l'SDK Places o l'API Places.

Aggiungere una nuova visualizzazione Swift

Aggiungi il codice seguente a un nuovo file Swift denominato 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()
}

Visualizza l'anteprima ed esegui l'app

Apri il riquadro di anteprima per visualizzare l'anteprima dell'app.

Per eseguire l'app, aggiungi un nuovo NavigationLink a GoogleMaps3DDemoApp.swift.

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

10. (Facoltativo) Per saperne di più

Animazioni della fotocamera avanzata

Alcuni casi d'uso richiedono un'animazione fluida lungo una sequenza o un elenco di posizioni o stati della videocamera, ad esempio un simulatore di volo o la riproduzione di una passeggiata o una corsa.

In questo passaggio imparerai a caricare un elenco di località da un file e ad animare ogni località in sequenza.

Visualizzazione mappa 3D dell'avvicinamento a Innsbruck

Carica un file contenente una sequenza di località.

Scarica flightpath.json dal repository di app di esempio di GitHub.

Crea una nuova cartella nel progetto Xcode denominata JSON.

Trascina flightpath.json nella cartella JSON in Xcode.

Imposta il target principale della tua app. Verifica che le impostazioni delle risorse del pacchetto di copia del progetto includano questo file.

Crea due nuovi file Swift nella tua app denominati FlightPathData.swift e FlightDataLoader.swift.

Copia il seguente codice nella tua app. Questo codice crea strutture e classi che leggono un file locale denominato "flighpath.json" e lo decodificano come JSON.

Le strutture FlightPathData e FlightPathLocation rappresentano la struttura di dati nel file JSON come oggetti Swift.

La classe FlightDataLoader legge i dati dal file e li decodifica. Adotta il protocollo ObservableObject per consentire all'app di osservare le modifiche ai suoi dati.

I dati analizzati vengono esposti tramite una proprietà pubblicata.

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

Anima la videocamera in ogni posizione

Per animare la fotocamera tra una sequenza di passaggi, utilizzerai un KeyframeAnimator.

Ogni Keyframe verrà creato come CubicKeyframe, in modo che le modifiche dello stato della videocamera siano animate in modo fluido.

L'utilizzo di flyCameraTo() causerebbe il "rimbalzo" della visualizzazione tra ogni località.

Innanzitutto, dichiara una videocamera chiamata "innsbruck" in 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)

Ora configura una nuova visualizzazione in un nuovo file denominato FlyAlongRoute.swift.

Importa SwiftUI e GoogleMaps3D. Aggiungi un Map e un Button all'interno di un VStack. Imposta Button per attivare/disattivare lo stato della variabile booleana animation.

Dichiara un oggetto State per FlightDataLoader, che caricherà il file JSON al momento dell'inizializzazione.

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

Crea i fotogrammi chiave

La procedura di base consiste nel creare una funzione che restituisce un nuovo frame nella sequenza di animazione. Ogni nuovo frame definisce lo stato successivo della fotocamera che l'animatore deve animare. Una volta creata questa funzione, chiamala con ogni posizione del file in sequenza.

Aggiungi due funzioni alla struttura FlyAlongRoute. La funzione makeKeyFrame restituisce un CubicKeyframe con uno stato della videocamera. La funzione makeCamera prende un passaggio nella sequenza di dati di volo e restituisce un oggetto Camera che lo rappresenta.

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

Mettere insieme l'animazione

Chiama keyframeAnimator dopo l'inizializzazione di Map e imposta i valori iniziali.

Avrai bisogno di uno stato iniziale della videocamera in base alla prima posizione nel percorso di volo.

L'animazione deve essere attivata in base allo stato di una variabile in modifica.

I contenuti di keyframeAnimator devono essere una mappa.

L'elenco effettivo dei fotogrammi chiave viene generato eseguendo un ciclo in ogni posizione del percorso di volo.

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

Visualizza l'anteprima ed esegui l'app.

Apri il riquadro di anteprima per visualizzare l'anteprima della visualizzazione.

Aggiungi un nuovo NavigationLink con destinazione FlightPathDemo() a GoogleMaps3DDemoApp.swift ed esegui l'app per provarlo.

11. Complimenti

Hai creato un'applicazione che

  • Aggiunge una mappa 3D di base alla tua app.
  • Aggiunge indicatori, linee, poligoni e modelli alla mappa.
  • Implementa il codice per controllare la fotocamera in modo da volare sulla mappa e intorno a posizioni specifiche.

Che cosa hai imparato

  • Come aggiungere il pacchetto GoogleMaps3D a un'app Xcode SwiftUI.
  • Come inizializzare una mappa 3D con una chiave API e una visualizzazione predefinita.
  • Come aggiungere indicatori, modelli 3D, linee e poligoni alla mappa.
  • Come controllare la videocamera per animare il movimento in un'altra posizione.
  • Come gestire gli eventi di clic sugli indicatori di luogo.

Passaggi successivi

  • Consulta la guida per gli sviluppatori per ulteriori dettagli su cosa puoi fare con l'SDK Maps 3D per iOS.
  • Aiutaci a creare i contenuti che ritieni più utili rispondendo al seguente sondaggio:

Quali altri codelab vorresti vedere?

Visualizzazione dei dati sulle mappe Scopri di più sulla personalizzazione dello stile delle mie mappe Creare interazioni 3D nelle mappe