1. Antes de comenzar
En este codelab, aprenderás a crear una app de mapas en 3D en SwiftUI con el SDK de Maps 3D para iOS.
Aprenderás a hacer lo siguiente:
- Cómo controlar la cámara para ver ubicaciones y volar por el mapa
- Cómo agregar marcadores y modelos
- Cómo dibujar líneas y polígonos
- Cómo controlar los clics de los usuarios en los marcadores de Place
Requisitos previos
- Un proyecto de Google Play Console con la facturación habilitada
- Una clave de API, que se puede restringir de forma opcional al SDK de Mapas 3D para iOS
- Conocimientos básicos sobre el desarrollo de iOS con SwiftUI
Actividades
- Configura Xcode y agrega el SDK con Swift Package Manager
- Configura tu app para usar una clave de API
- Agrega un mapa 3D básico a tu app
- Controla la cámara para volar a ubicaciones específicas y alrededor de ellas
- Agrega marcadores, líneas, polígonos y modelos a tu mapa
Requisitos
- Xcode 15 o una versión posterior
2. Prepárate
Para el siguiente paso, debes habilitar el SDK de Maps 3D para iOS.
Configura Google Maps Platform
Si todavía no tienes una cuenta de Google Cloud Platform y un proyecto con la facturación habilitada, consulta la guía Cómo comenzar a utilizar Google Maps Platform para crear una cuenta de facturación y un proyecto.
- En Cloud Console, haz clic en el menú desplegable del proyecto y selecciona el proyecto que deseas usar para este codelab.
- Habilita las API y los SDK de Google Maps Platform necesarios para este codelab en Google Cloud Marketplace. Para hacerlo, sigue los pasos que se indican en este video o esta documentación.
- Genera una clave de API en la página Credenciales de Cloud Console. Puedes seguir los pasos que se indican en este video o esta documentación. Todas las solicitudes a Google Maps Platform requieren una clave de API.
Habilita el SDK de Mapas 3D para iOS
Puedes encontrar el SDK de Maps 3D para iOS en el vínculo del menú Google Maps Platform > APIs y servicios de la consola.
Haz clic en Habilita para habilitar la API en el proyecto seleccionado.
3. Crea una app básica de SwiftUI
Nota: Puedes encontrar el código de la solución para cada paso en el repositorio de la app de ejemplo del codelab en GitHub .
Crea una app nueva en Xcode.
El código de este paso se puede encontrar en la carpeta GoogleMaps3DDemo en GitHub.
Abre Xcode y crea una app nueva. Especifica SwiftUI.
Llama a tu app GoogleMaps3DDemo
, con un nombre de paquete com.example.GoogleMaps3DDemo
.
Importa la biblioteca de GoogleMaps3D a tu proyecto
Agrega el SDK a tu proyecto con Swift Package Manager.
En tu proyecto o lugar de trabajo de Xcode, ve a File > Add Package Dependencies. Ingresa https://github.com/googlemaps/ios-maps-3d-sdk como la URL, presiona Intro para extraer el paquete y haz clic en "Agregar paquete".
En la ventana Choose Package Products, verifica que se agregará GoogleMaps3D
a tu destino principal designado. Cuando termines, haz clic en Agregar paquete.
Para verificar la instalación, navega al panel General de tu segmentación. En Frameworks, bibliotecas y contenido incorporado, deberías ver los paquetes instalados. También puedes ver la sección Dependencias de paquetes del Navegador de proyectos para verificar el paquete y su versión.
Agrega tu clave de API
Puedes codificar tu clave de API en la app, pero no se recomienda. Agregar un archivo de configuración te permite mantener la clave de API en secreto y evitar que se registre en el control de código fuente.
Crea un nuevo archivo de configuración en la carpeta raíz del proyecto
En Xcode, asegúrate de que estás viendo la ventana del explorador de proyectos. Haz clic con el botón derecho en la raíz del proyecto y selecciona "New File from Template". Desplázate hasta que veas "Archivo de configuración". Selecciona esta opción y haz clic en "Siguiente". Asigna el nombre Config.xcconfig
al archivo y asegúrate de que esté seleccionada la carpeta raíz del proyecto. Haz clic en "Crear" para crear el archivo.
En el editor, agrega una línea al archivo de configuración de la siguiente manera: MAPS_API_KEY = YOUR_API_KEY
Reemplaza YOUR_API_KEY
por tu clave de API.
Agrega este parámetro de configuración a Info.plist
.
Para ello, selecciona la raíz del proyecto y haz clic en la pestaña “Información”.
Agrega una propiedad nueva llamada MAPS_API_KEY
con un valor de $(MAPS_API_KEY)
.
El código de la app de ejemplo tiene un archivo Info.plist
que especifica esta propiedad.
Agrega un mapa
Abre el archivo llamado GoogleMaps3DDemoApp.swift
. Este es el punto de entrada y la navegación principal de tu app.
Llama a ContentView()
, que muestra un mensaje de Hello World.
Abre ContentView.swift
en el editor.
Agrega una sentencia import
para GoogleMaps3D
.
Borra el código dentro del bloque de código var body: some View {}
. Declara un nuevo Map()
dentro de body
.
La configuración mínima que necesitas para inicializar un Map
es un MapMode
. Tiene dos valores posibles:
.hybrid
: Imágenes satelitales con rutas y etiquetas.satellite
: Solo imágenes satelitales.
Elija .hybrid
.
Tu archivo ContentView.swift
debería tener el siguiente aspecto:
import GoogleMaps3D
import SwiftUI
@main
struct ContentView: View {
var body: some View {
Map(mode: .hybrid)
}
}
Establece tu clave de API.
La clave de API se debe establecer antes de que se inicialice el mapa.
Para ello, establece Map.apiKey
en el controlador de eventos init()
de cualquier View
que contenga un mapa. También puedes configurarlo en GoogleMaps3DDemoApp.swift
antes de que llame a ContentView()
.
En GoogleMaps3DDemoApp.swift
, establece Map.apiKey
en el controlador de eventos onAppear
de WindowGroup
.
Recupera tu clave de API del archivo de configuración
Usa Bundle.main.infoDictionary
para acceder a la configuración de MAPS_API_KEY
que creaste en tu archivo de configuración.
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
}
}
}
Compila y ejecuta tu app para verificar que se cargue correctamente. Deberías ver un mapa del mundo.
4. Cómo usar una cámara para controlar la vista de mapa
Crea un objeto de estado de la cámara
Las vistas de mapas en 3D se controlan con la clase Camera
. En este paso, aprenderás a especificar la ubicación, la altitud, el rumbo, la inclinación, el balanceo y el rango para personalizar la vista del mapa.
Crea una clase Helpers para almacenar la configuración de la cámara
Agrega un nuevo archivo vacío llamado MapHelpers.swift
. En tu archivo nuevo, importa GoogleMaps3D
y agrega una extensión a la clase Camera
. Agrega una variable llamada sanFrancisco
. Inicializa esta variable como un nuevo objeto Camera
. Busca la cámara en latitude: 37.39, longitude: -122.08
.
import GoogleMaps3D
extension Camera {
public static var sanFrancisco: Camera = .init(latitude: 37.39, longitude: -122.08)
}
Agrega una nueva vista a tu app
Crea un archivo nuevo llamado CameraDemo.swift
de la siguiente manera: Agrega el esquema básico de una nueva vista de SwiftUI al archivo.
Agrega una variable @State
llamada camera
de tipo Camera
. Inicialízala en la cámara sanFrancisco
que acabas de definir.
El uso de @State
te permite vincular el mapa al estado de la cámara y usarlo como fuente de confianza.
@State var camera: Camera = .sanFrancisco
Cambia la llamada a función Map()
para incluir una propiedad camera
. Usa la vinculación de estado de la cámara $camera
para inicializar la propiedad camera
en tu objeto @State
de la cámara (.sanFrancisco
).
import SwiftUI
import GoogleMaps3D
struct CameraDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid)
}
}
}
Agrega una IU de navegación básica a tu app
Agrega un NavigationView
al punto de entrada principal de la app, GoogleMaps3DDemoApp.swift
.
Esto permitirá que los usuarios vean una lista de demos y hagan clic en cada una para abrirlas.
Edita GoogleMaps3DDemoApp.swift
para agregar un NavigationView
nuevo.
Agrega un List
que contenga dos declaraciones NavigationLink
.
El primer NavigationLink
debe abrir ContentView()
con una descripción Text
Basic Map
.
El segundo NavigationLink
debería abrir CameraDemo()
.
...
NavigationView {
List {
NavigationLink(destination: ContentView()) {
Text("Basic Map")
}
NavigationLink(destination: CameraDemo()) {
Text("Camera Demo")
}
}
}
...
Cómo agregar una vista previa de Xcode
Las vistas previas son una función potente de Xcode que te permite ver tu app y, además, interactuar con ella a medida que realizas cambios.
Para agregar una vista previa, abre CameraDemo.swift
. Agrega un bloque de código #Preview {}
fuera de struct
.
#Preview {
CameraDemo()
}
Abre o actualiza el panel de vista previa en Xcode. El mapa debería mostrar San Francisco.
Configura vistas 3D personalizadas
Puedes especificar parámetros adicionales para controlar la cámara:
heading
: Es el rumbo en grados desde el norte hacia el que se debe apuntar la cámara.tilt
: Es el ángulo de inclinación en grados, donde 0 es directamente hacia arriba y 90 es horizontal.roll
: Es el ángulo de balanceo alrededor del plano vertical de la cámara, en grados.range
: Es la distancia en metros de la cámara desde la ubicación de latitud y longitud.altitude
: Es la altura de la cámara sobre el nivel del mar.
Si no proporcionas ninguno de estos parámetros adicionales, se usarán los valores predeterminados.
Para que la vista de la cámara muestre más datos en 3D, establece los parámetros iniciales para mostrar una vista más cercana y inclinada.
Edita el Camera
que definiste en MapHelpers.swift
para incluir valores para altitude
, heading
, tilt
, roll
y 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)
Compila y ejecuta la app para ver y explorar la nueva vista en 3D.
5. Animaciones básicas de la cámara
Hasta ahora, usaste la cámara para especificar una sola ubicación con una inclinación, altitud, rumbo y rango. En este paso, aprenderás a mover la vista de la cámara animando estas propiedades de un estado inicial a uno nuevo.
Cómo volar a una ubicación
Usarás el método Map.flyCameraTo()
para animar la cámara de la ubicación inicial a una nueva.
El método flyCameraTo()
toma una serie de parámetros:
- un
Camera
que representa la ubicación de destino. duration
: Es la duración de la animación, en segundos.trigger
: Es un objeto observable que activará la animación cuando cambie su estado.completion
: Es el código que se ejecutará cuando se complete la animación.
Define una ubicación a la que volar
Abre el archivo MapHelpers.swift
.
Define un nuevo objeto de cámara para mostrar Seattle.
public static var seattle: Camera = .init(latitude:
47.6210296,longitude: -122.3496903, heading: 149.0, tilt: 77.0, roll: 0.0, range: 4000)
Agrega un botón para activar la animación.
Abre tu CameraDemo.swift
. Declara una nueva variable booleana dentro de struct
.
Llámala animate
con un valor inicial de false
.
@State private var animate: Bool = false
Agrega un Button
debajo de VStack
. El Button
iniciará la animación del mapa.
Dale al Button
un Text
adecuado, como "Comenzar a volar".
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") {
}
}
}
}
En el cierre del botón, agrega código para activar o desactivar el estado de la variable animate
.
Button("Start Flying") {
animate.toggle()
}
Inicia la animación.
Agrega el código para activar la animación flyCameraTo()
cuando cambie el estado de la variable animate
.
var body: some View {
VStack{
Map(camera: $camera, mode: .hybrid)
.flyCameraTo(
.seattle,
duration: 5,
trigger: animate,
completion: { }
)
Button("Start Flying") {
animate.toggle()
}
}
}
Cómo volar alrededor de una ubicación
Puedes volar alrededor de una ubicación con el método Map.flyCameraAround()
. Este método toma varios parámetros:
- un
Camera
que define la ubicación y la vista. - un
duration
en segundos. rounds
: Es la cantidad de veces que se debe repetir la animación.trigger
: Es un objeto observable que activará la animación.callback
: Es el código que se ejecutará cuando se ejecute la animación.
Define una nueva variable @State
llamada flyAround
, con un valor inicial de false
.
Cuando lo hayas hecho, agrega una llamada a flyCameraAround()
inmediatamente después de la llamada al método flyCameraTo()
.
La duración del recorrido debe ser relativamente larga para que la vista cambie sin problemas.
Asegúrate de activar la animación flyCameraAround()
cambiando el estado del objeto del activador cuando se complete flyCameraTo()
.
Tu código debería verse de la siguiente manera:
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()
}
Obtén una vista previa de la app o ejecútala para ver que la cámara vuela alrededor del destino una vez que se complete la animación de flyCameraTo()
.
6. Agrega un marcador a tu mapa.
En este paso, aprenderás a dibujar un pin de marcador en el mapa.
Crearás un objeto Marker
y lo agregarás a tu mapa. El SDK usará un ícono predeterminado para el marcador. Por último, ajustarás la altitud del marcador y otras propiedades para cambiar su apariencia.
Crea una nueva vista de SwiftUI para tu demo de marcadores.
Agrega un nuevo archivo Swift a tu proyecto. Asígnale el nombre MarkerDemo.swift
.
Agrega el esquema de una vista de SwiftUI y, luego, inicializa el mapa como lo hiciste en tu CameraDemo
.
import SwiftUI
import GoogleMaps3D
struct MarkerDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid)
}
}
}
Cómo inicializar un objeto Marker
Declara una nueva variable de marcador llamada mapMarker
. En la parte superior del bloque de código struct
en MarkerDemo.swift
.
Coloca la definición en la línea debajo de la declaración camera
. Este código de muestra inicializa todas las propiedades disponibles.
@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"
)
Agrega el marcador a tu mapa.
Para dibujar el marcador, agrégalo a un cierre que se llame cuando se cree el mapa.
struct MarkerDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
mapMarker
}
}
}
}
Agrega un NavigationLink
nuevo a GoogleMaps3DDemoApp.swift
con un destino de MarkerDemo()
y Text
que lo describa como "Demostración de marcadores".
...
NavigationView {
List {
NavigationLink(destination: Map()) {
Text("Basic Map")
}
NavigationLink(destination: CameraDemo()) {
Text("Camera Demo")
}
NavigationLink(destination: MarkerDemo()) {
Text("Marker Demo")
}
}
}
...
Obtén una vista previa de tu app y ejecútala
Actualiza la vista previa o ejecuta la app para ver el marcador.
Marcadores extruidos
Los marcadores se pueden colocar sobre el suelo o la malla 3D con altitude
y altitudeMode
.
Copia la declaración mapMarker
en MarkerDemo.swift
a una nueva variable Marker
llamada extrudedMarker
.
Establece un valor distinto de cero para altitude
, 50 es suficiente.
Cambia altitudeMode
a .relativeToMesh
y establece extruded
en true
. Usa latitude
y longitude
del fragmento de código aquí para colocar el marcador en la parte superior de un rascacielos.
@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"
)
Vuelve a ejecutar o a obtener una vista previa de la app. El marcador debe aparecer en la parte superior de un edificio 3D.
7. Agrega un modelo a tu mapa.
Se puede agregar un Model
de la misma manera que un Marker
. Necesitarás un archivo de modelo al que se pueda acceder mediante una URL o agregarlo como archivo local en tu proyecto. En este paso, usaremos un archivo local que puedes descargar del repositorio de GitHub de este codelab.
Agrega un archivo de modelo a tu proyecto
Crea una carpeta nueva en tu proyecto de Xcode llamada Models
.
Descarga el modelo desde el repositorio de apps de ejemplo de GitHub. Para agregarlo a tu proyecto, arrástralo a la carpeta nueva en la vista del proyecto de Xcode.
Asegúrate de establecer el objetivo principal para tu app.
Revisa la configuración de Build Phases > Copy Bundle Resources de tu proyecto. El archivo del modelo debe estar en la lista de recursos copiados en el paquete. Si no está allí, haz clic en "+" para agregarla.
Agrega el modelo a tu app.
Crea un archivo SwiftUI nuevo llamado ModelDemo.swift
.
Agrega sentencias import
para SwiftUI
y GoogleMaps3D
como en los pasos anteriores.
Declara un Map
dentro de un VStack
en tu body
.
import SwiftUI
import GoogleMaps3D
struct ModelDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
}
}
}
}
Obtén la ruta de acceso del modelo de tu paquete. Agrega el código para esto fuera de struct
.
private let fileUrl = Bundle.main.url(forResource: "balloon", withExtension: "glb")
Declara una variable para tu modelo dentro de la estructura.
Proporciona un valor predeterminado en caso de que no se proporcione 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. Usa el modelo con tu mapa.
Al igual que con la adición de un Marker
, solo proporciona la referencia a tu Model
en la declaración Map
.
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
balloonModel
}
}
}
Obtén una vista previa de tu app y ejecútala
Agrega un nuevo NavigationLink
a GoogleMaps3DDemoApp.swift
, con un destino de ModelDemo()
y la Text
"Demo de modelos".
...
NavigationLink(destination: ModelDemo()) {
Text("Model Demo")
}
...
Actualiza la vista previa o ejecuta la app para ver el modelo.
8. Dibuja una línea y un polígono en tu mapa.
En este paso, aprenderás a agregar líneas y formas de polígonos a tu mapa 3D.
Para simplificar, definirás las formas como arrays de objetos LatLngAltitude
. En una aplicación real, los datos se pueden cargar desde un archivo, una llamada a la API o una base de datos.
Crea algunos objetos de forma para administrar los datos de forma.
Agrega una nueva definición de Camera
a MapHelpers.swift
que apunte al centro de San Francisco.
public static var downtownSanFrancisco: Camera = .init(latitude: 37.7905, longitude: -122.3989, heading: 25, tilt: 71, range: 2500)
Agrega un archivo nuevo a tu proyecto llamado ShapesDemo.swift
. Agrega un struct
llamado ShapesDemo
que implemente el protocolo View
y agrégale un body
.
struct ShapesDemo: View {
@State var camera: Camera = .downtownSanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
}
}
}
}
Las clases que usarás para administrar los datos de forma son Polyline
y Polygon
. Abre ShapesDemo.swift
y agrégalos a struct
de la siguiente manera.
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) )
Observa los parámetros de inicialización que se usan.
altitudeMode: .relativeToGround
se usa para extruir los polígonos a una altura específica sobre el suelo.altitudeMode: .clampToGround
se usa para que la polilínea siga la forma de la superficie de la Tierra.- Los estilos se establecen en los objetos
Polygon
encadenando una llamada de método astyleOptions()
después de llamar a.init()
.
Agrega las formas al mapa
Al igual que en los pasos anteriores, las formas se pueden agregar directamente al cierre Map
. Crea tu Map
dentro de un VStack
.
...
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
polyline
originPolygon
destinationPolygon
}
}
}
...
Obtén una vista previa de tu app y ejecútala
Agrega código de vista previa y, luego, inspecciona tu app en el panel de vista previa de Xcode.
#Preview {
ShapesDemo()
}
Para ejecutar la app, agrega un nuevo NavigationLink
a GoogleMaps3DDemoApp.swift
que abra la nueva vista de demostración.
...
NavigationLink(destination: ShapesDemo()) {
Text("Shapes Demo")
}
...
Ejecuta la app y explora las formas que agregaste.
9. Controla los eventos de presión en los marcadores de Place
En este paso, aprenderás a responder a los toques del usuario en los marcadores de Place.
Nota: Para ver los marcadores de Place en el mapa, debes establecer MapMode
en .hybrid
.
Para controlar el toque, debes implementar el método Map.onPlaceTap
.
El evento onPlaceTap
proporciona un objeto PlaceTapInfo
desde el que puedes obtener el ID de Place del marcador de Place que se presionó.
Puedes usar el ID de lugar para buscar más detalles con el SDK de Places o la API de Places.
Agrega una nueva vista de Swift
Agrega el siguiente código a un nuevo archivo Swift llamado 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()
}
Obtén una vista previa y ejecuta tu app
Abre el panel de vista previa para obtener una vista previa de la app.
Para ejecutar la app, agrega un NavigationLink
nuevo a GoogleMaps3DDemoApp.swift
.
...
NavigationLink(destination: PlaceTapDemo()) {
Text("Place Tap Demo")
}
...
10. (Opcional) Ve más allá
Animaciones de cámara avanzadas
Algunos casos de uso requieren animaciones fluidas a lo largo de una secuencia o lista de ubicaciones o estados de la cámara, por ejemplo, un simulador de vuelo o la repetición de una caminata o una carrera.
En este paso, aprenderás a cargar una lista de ubicaciones desde un archivo y a animar cada ubicación en secuencia.
Carga un archivo que contenga una secuencia de ubicaciones.
Descarga flightpath.json
del repositorio de apps de ejemplo de GitHub.
Crea una carpeta nueva en tu proyecto de Xcode llamada JSON
.
Arrastra flightpath.json
a la carpeta JSON
en Xcode.
Establece el objetivo principal de tu app. Verifica que la configuración de Copy Bundle Resources de tu proyecto incluya este archivo.
Crea dos archivos Swift nuevos en tu app llamados FlightPathData.swift
y FlightDataLoader.swift
.
Copia el siguiente código en tu app. Este código crea estructuras y clases que leen un archivo local llamado "flighpath.json" y lo decodifican como JSON.
Las estructuras FlightPathData
y FlightPathLocation
representan la estructura de datos en el archivo JSON como objetos Swift.
La clase FlightDataLoader
lee los datos del archivo y los decodifica. Adopta el protocolo ObservableObject
para permitir que tu app observe los cambios en sus datos.
Los datos analizados se exponen a través de una propiedad publicada.
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 cámara a lo largo de cada ubicación
Para animar la cámara entre una secuencia de pasos, usarás un KeyframeAnimator
.
Cada Keyframe
se creará como un CubicKeyframe
, de modo que los cambios en el estado de la cámara se animen de forma fluida.
El uso de flyCameraTo()
haría que la vista "rebote" entre cada ubicación.
Primero, declara una cámara llamada "innsbruck" en 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)
Ahora, configura una nueva vista en un archivo nuevo llamado FlyAlongRoute.swift
.
Importa SwiftUI
y GoogleMaps3D
. Agrega un Map
y un Button
dentro de un VStack
. Configura Button
para activar o desactivar el estado de la variable booleana animation
.
Declara un objeto State
para FlightDataLoader
, que cargará el archivo JSON cuando se inicialice.
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 los fotogramas clave
El proceso básico es crear una función que devuelva un fotograma nuevo en la secuencia de animación. Cada fotograma nuevo define el siguiente estado de la cámara para que el animador lo anime. Una vez que se cree esta función, llámala con cada ubicación del archivo en secuencia.
Agrega dos funciones a tu struct FlyAlongRoute
. La función makeKeyFrame
muestra un CubicKeyframe
con un estado de la cámara. La función makeCamera
toma un paso en la secuencia de datos de vuelo y muestra un objeto Camera
que representa el paso.
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
)
}
Cómo crear la animación
Llama a keyframeAnimator
después de la inicialización de Map
y establece los valores iniciales.
Necesitarás un estado inicial de la cámara basado en la primera ubicación de la ruta de vuelo.
La animación debe activarse en función de un estado de cambio de variable.
El contenido de keyframeAnimator
debe ser un mapa.
La lista real de fotogramas clave se genera mediante un bucle en cada ubicación de la ruta de vuelo.
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])
}
})
}
)
}
Obtén una vista previa de la app y ejecútala.
Abre el panel de vista previa para obtener una vista previa de tu vista.
Agrega un NavigationLink
nuevo con un destino de FlightPathDemo()
a GoogleMaps3DDemoApp.swift
y ejecuta la app para probarlo.
11. Felicitaciones
Compilaste correctamente una aplicación que hace lo siguiente:
- Agrega un mapa 3D básico a tu app.
- Agrega marcadores, líneas, polígonos y modelos a tu mapa.
- Implementa código para controlar la cámara y volar por el mapa y alrededor de ubicaciones específicas.
Qué aprendiste
- Cómo agregar el paquete
GoogleMaps3D
a una app de SwiftUI de Xcode - Cómo inicializar un mapa 3D con una clave de API y una vista predeterminada
- Cómo agregar marcadores, modelos 3D, líneas y polígonos a tu mapa
- Cómo controlar la cámara para animar el movimiento a otra ubicación
- Cómo controlar los eventos de clic en los marcadores de Place
Próximos pasos
- Consulta la guía para desarrolladores para obtener más detalles sobre lo que puedes hacer con el SDK de Mapas 3D para iOS.
- Responde la siguiente encuesta para ayudarnos a crear el contenido que te resulte más útil: