Agrega un mapa a tu app para iOS con SwiftUI (Swift)

1. Antes de comenzar

En este codelab, aprenderás a usar el SDK de Maps para iOS con SwiftUI.

screenshot-iphone-12-black@2x.png

Requisitos previos

  • Conocimientos básicos de Swift
  • Conocimientos básicos de SwiftUI

Actividades

  • Habilita el SDK de Maps para iOS y utilízalo a fin de agregar mapas de Google Maps a una app para iOS con SwiftUI.
  • Agrega marcadores al mapa.
  • Pasa el estado de una vista de SwiftUI a un objeto GMSMapView y viceversa.

Otros requisitos

2. Prepárate

Para el siguiente paso, debes habilitar el SDK de Maps 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.

  1. En Cloud Console, haz clic en el menú desplegable del proyecto y selecciona el proyecto que deseas usar para este codelab.

  1. 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.
  2. 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.

3. Descarga el código de inicio

Para que puedas comenzar lo más rápido posible, te ofrecemos un código inicial que te ayudará a seguir este codelab. Puedes pasar directamente a la solución, pero si quieres ir paso a paso para ver cómo crearla tú mismo, sigue leyendo.

  1. Si tienes git instalado, clona el repositorio.
git clone https://github.com/googlecodelabs/maps-ios-swiftui.git

También puedes hacer clic en el botón siguiente para descargar el código fuente.

  1. Después de obtener el código, en una terminal cd dentro del directorio starter/GoogleMapsSwiftUI, haz lo siguiente:
  2. Ejecuta carthage update --platform iOS a fin de descargar el SDK de Maps para iOS.
  3. Por último, abre el archivo GoogleMapsSwiftUI.xcodeproj en Xcode.

4. Descripción general del código

En el proyecto inicial que descargaste, se proporcionaron y se implementaron las siguientes clases para ti:

  • AppDelegate: el UIApplicationDelegate de la app (aquí se inicializará el SDK de Maps para iOS)
  • City: una estructura que representa una ciudad (contiene el nombre y la coordenada de la ciudad)
  • MapViewController: un UIKit UIViewController simple que contiene un mapa de Google Maps (GMSMapView)
  • SceneDelegate: el UIWindowSceneDelegate de la app desde el que se crea una instancia de ContentView

Además, las siguientes clases tienen implementaciones parciales que completarás al final de este codelab:

  • ContentView: la vista de SwiftUI de nivel superior que contiene a tu app
  • MapViewControllerBridge: una clase que vincula una vista de UIKit a una vista de SwiftUI (específicamente, esta es la clase que hará que MapViewController sea accesible en SwiftUI)

5. Cómo usar SwiftUI en comparación con UIKit

SwiftUI se introdujo en iOS 13 como un marco de trabajo de IU alternativo en lugar de UIKit a fin de desarrollar apps para iOS. En comparación con su predecesor, SwiftUI ofrece varias ventajas. A continuación, te mostramos algunas de ellas:

  • Las vistas se actualizan automáticamente cuando cambia el estado. Si usas los objetos denominados State, cualquier cambio en el valor subyacente que contienen hará que la IU se actualice automáticamente.
  • Las vistas previas en vivo permiten un desarrollo más rápido. Las vistas previas en vivo minimizan la necesidad de compilar e implementar código en un emulador con el fin de que se produzcan cambios visuales, ya que se puede obtener fácilmente una vista previa de SwiftUI en Xcode.
  • La fuente de confianza está en Swift. Todas las vistas en SwiftUI se declaran en Swift, por lo que ya no es necesario usar el compilador de interfaz.
  • SwiftUI interactúa con UIKit. La interoperabilidad con UIKit garantiza que las apps existentes puedan usar SwiftUI de forma incremental con sus vistas actuales. Además, las bibliotecas que aún no admiten SwiftUI, como el SDK de Maps para iOS, sí se pueden usar en SwiftUI.

También existen algunas desventajas:

  • SwiftUI solo está disponible en iOS 13 y versiones posteriores.
  • La jerarquía de vistas no se puede examinar en las vistas previas de Xcode.

Estado y flujo de datos de SwiftUI

SwiftUI ofrece una novedosa manera de crear IU con un enfoque declarativo: le dices a SwiftUI cuál quieres que sea la vista junto con todos sus diferentes estados, y el sistema hará el resto. SwiftUI se encarga de actualizar la vista cuando el estado subyacente cambia debido a un evento o a una acción del usuario. Este diseño se conoce comúnmente como flujo de datos unidireccional. Si bien los detalles específicos de este diseño están fuera del alcance de este codelab, te recomendamos que leas en detalle cómo funciona esto en la documentación de Apple sobre el estado y el flujo de datos.

Cómo conectar UIKit y SwiftUI con UIViewRepresentable o UIViewControllerRepresentable

Dado que el SDK de Maps para iOS se desarrolló a partir de UIKit y aún no proporciona una vista compatible con SwiftUI, para usarlo en SwiftUI, se requiere de UIViewRepresentable o UIViewControllerRepresentable. Estos protocolos permiten que SwiftUI incluya las UIView y los UIViewController compilados en UIKit, respectivamente. Si bien puedes usar cualquiera de los protocolos para agregar un mapa de Google Maps a una vista de SwiftUI, en el siguiente paso, analizaremos cómo usar UIViewControllerRepresentable para incluir un UIViewController que contenga un mapa.

6. Agrega un mapa

En esta sección, agregarás mapas de Google Maps a una vista de SwiftUI.

add-a-map-screenshot@2x.png

Agrega tu clave de API

Debes proporcionar al SDK de Maps para iOS la clave de API que creaste en un paso anterior a fin de asociar tu cuenta con el mapa que se mostraría en la app.

Para proporcionar tu clave de API, abre el archivo AppDelegate.swift y navega al método application(_, didFinishLaunchingWithOptions). Actualmente, el SDK se inicializa a través de GMSServices.provideAPIKey() con la string "YOUR_API_KEY". Reemplaza esa string por la clave de API. Al completar este paso, se inicializará el SDK de Maps para iOS cuando se inicie la aplicación.

Agrega un mapa de Google Maps con MapViewControllerBridge

Ahora que se proporciona tu clave de API al SDK, el siguiente paso es mostrar el mapa en la app.

Actualmente, el controlador de vista MapViewController que se proporciona en el código de inicio contiene GMSMapView en su vista. Sin embargo, como este controlador de vista se creó en UIKit, deberás conectar esta clase a SwiftUI para que se pueda usar dentro de ContentView. Para ello, sigue estos pasos:

  1. Abre el archivo MapViewControllerBridge en Xcode.

Esta clase se ajusta a UIViewControllerRepresentable, que es el protocolo que se necesita para la unión con un UIViewController de UIKit, de modo que se pueda usar como una vista de SwiftUI. En otras palabras, cumplir con este protocolo te permite establecer una vinculación entre una vista de UIKit y una de SwiftUI, y requiere de los siguientes dos métodos:

  • makeUIViewController(context): SwiftUI llama a este método para crear el UIViewController subyacente. Aquí, crearás una instancia de UIViewController y la pasarás a su estado inicial.
  • updateUIViewController(_, context): SwiftUI llama a este método cada vez que cambia el estado. Aquí es donde harías cualquier modificación en el UIViewController subyacente a fin de reaccionar en respuesta al cambio de estado.
  1. Crea un MapViewController

Dentro de la función makeUIViewController(context), crea una instancia nueva de MapViewController y muéstrala como resultado. Después de hacerlo, tu MapViewControllerBridge debería verse de la siguiente manera:

MapViewControllerBridge

import GoogleMaps
import SwiftUI

struct MapViewControllerBridge: UIViewControllerRepresentable {

  func makeUIViewController(context: Context) -> MapViewController {
    return MapViewController()
  }

  func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
  }
}

Usa MapViewControllerBridge en ContentView

Ahora que MapViewControllerBridge está creando una instancia de MapViewController, el siguiente paso es usar esta estructura dentro de ContentView para mostrar un mapa.

  1. Abre el archivo ContentView en Xcode.

Las instancias de ContentView se crean en SceneDelegate y contienen la vista de las apps de nivel superior. El mapa se agregará desde este archivo.

  1. Crea un MapViewControllerBridge dentro de la propiedad body.

En la propiedad body de este archivo, ya se proporcionó y se implementó un ZStack para ti. Actualmente, ZStack contiene una lista de ciudades interactiva y arrastrable que usarás en un paso posterior. Por ahora, dentro de ZStack, crea un MapViewControllerBridge como la primera vista secundaria de ZStack, de modo que se muestre un mapa en la app, detrás de la vista de la lista de ciudades. Después de este paso, el contenido de la propiedad body dentro de ContentView debería verse de la siguiente manera:

ContentView

var body: some View {

  let scrollViewHeight: CGFloat = 80

  GeometryReader { geometry in
    ZStack(alignment: .top) {
      // Map
      MapViewControllerBridge()

      // Cities List
      CitiesList(markers: $markers) { (marker) in
        guard self.selectedMarker != marker else { return }
        self.selectedMarker = marker
        self.zoomInCenter = false
        self.expandList = false
      }  handleAction: {
        self.expandList.toggle()
      } // ...
    }
  }
}
  1. Ahora, ejecuta la app. Deberías ver que el mapa se carga en la pantalla de tu dispositivo, junto con una lista de ciudades que se puede arrastrar hacia la parte inferior de la pantalla.

7. Agrega marcadores al mapa

En el paso anterior, agregaste un mapa junto a una lista interactiva que muestra distintas ciudades. En esta sección, agregarás marcadores para cada ciudad de esa lista.

map-with-markers@2x.png

Marcadores como estado

Actualmente, ContentView declara una propiedad denominada markers, que es una lista de GMSMarker que representa cada ciudad declarada en la propiedad estática cities. Ten en cuenta que esta propiedad está anotada con el wrapper State de la propiedad de SwiftUI a fin de indicar que debe administrarse mediante SwiftUI. Por lo tanto, si se detecta algún cambio en esta propiedad, como agregar o quitar un marcador, se actualizarán las vistas que usen este estado.

ContentView

  static let cities = [
    City(name: "San Francisco", coordinate: CLLocationCoordinate2D(latitude: 37.7576, longitude: -122.4194)),
    City(name: "Seattle", coordinate: CLLocationCoordinate2D(latitude: 47.6131742, longitude: -122.4824903)),
    City(name: "Singapore", coordinate: CLLocationCoordinate2D(latitude: 1.3440852, longitude: 103.6836164)),
    City(name: "Sydney", coordinate: CLLocationCoordinate2D(latitude: -33.8473552, longitude: 150.6511076)),
    City(name: "Tokyo", coordinate: CLLocationCoordinate2D(latitude: 35.6684411, longitude: 139.6004407))
  ]

  /// State for markers displayed on the map for each city in `cities`
  @State var markers: [GMSMarker] = cities.map {
    let marker = GMSMarker(position: $0.coordinate)
    marker.title = $0.name
    return marker
  }

Observa que ContentView usa la propiedad markers para renderizar la lista de ciudades; para ello, la pasa a la clase CitiesList.

CitiesList

struct CitiesList: View {

  @Binding var markers: [GMSMarker]

  var body: some View {
    GeometryReader { geometry in
      VStack(spacing: 0) {
        // ...
        // List of Cities
        List {
          ForEach(0..<self.markers.count) { id in
            let marker = self.markers[id]
            Button(action: {
              buttonAction(marker)
            }) {
              Text(marker.title ?? "")
            }
          }
        }.frame(maxWidth: .infinity)
      }
    }
  }
}

Pasa el estado a MapViewControllerBridge a través de una vinculación

Además de la lista de ciudades que muestra datos de la propiedad markers, pasa esta propiedad a la estructura de MapViewControllerBridge, de modo que se pueda usar a fin de mostrar esos marcadores en el mapa. Para eso, sigue estos pasos:

  1. Declara una nueva propiedad markers dentro de MapViewControllerBridge con la anotación @Binding.

MapViewControllerBridge

struct MapViewControllerBridge: : UIViewControllerRepresentable {
  @Binding var markers: [GMSMarker]
  // ...
}
  1. En MapViewControllerBridge, actualiza el método updateUIViewController(_, context) para poder usar la propiedad markers.

Como se mencionó en el paso anterior, SwiftUI llamará a updateUIViewController(_, context) cada vez que cambie el estado. Debemos actualizar el mapa dentro de este método con el fin de que se muestren los marcadores en markers. Para ello, deberás actualizar la propiedad map de cada marcador. Después de completar este paso, tu MapViewControllerBridge debería verse así:

import GoogleMaps
import SwiftUI

struct MapViewControllerBridge: UIViewControllerRepresentable {

  @Binding var markers: [GMSMarker]

  func makeUIViewController(context: Context) -> MapViewController {
    return MapViewController()
  }

  func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
    // Update the map for each marker
    markers.forEach { $0.map = uiViewController.map }
  }
}
  1. Pasa la propiedad markers de ContentView a MapViewControllerBridge

Como agregaste una propiedad nueva en MapViewControllerBridge, ahora se requiere que se pase el valor de esta propiedad en el inicializador de MapViewControllerBridge. Por lo tanto, si intentas compilar la app, notarás que no será posible. Para corregir esto, actualiza la ContentView en donde se crea MapViewControllerBridge y pasa la propiedad markers de la siguiente manera:

struct ContentView: View {
  // ...
  var body: some View {
    // ...
    GeometryReader { geometry in
      ZStack(alignment: .top) {
        // Map
        MapViewControllerBridge(markers: $markers)
        // ...
      }
    }
  }
}

Observa que el prefijo $ se usó para pasar markers a MapViewControllerBridge, ya que espera una propiedad vinculada. $ es un prefijo reservado para usar con wrappers de propiedades de Swift. Cuando se aplica a un estado, muestra una vinculación.

  1. Ejecuta la app para ver los marcadores que se muestran en el mapa.

8. Crea una animación de la ciudad seleccionada

En el paso anterior, agregaste marcadores a un mapa pasando el estado de una vista de SwiftUI a otra. En este paso, animarás la imagen de una ciudad o de un marcador después de que se haya presionado en la lista interactiva. Para realizar la animación, debes reaccionar a los cambios de un estado modificando la posición de la cámara del mapa cuando se produzcan los cambios. Para obtener más información sobre el concepto de la cámara del mapa, consulta Cámara y vista.

animate-city@2x.png

Crea una animación en el mapa de una ciudad seleccionada

Para crear una animación en el mapa de una ciudad seleccionada, completa este paso:

  1. Define una nueva vinculación en MapViewControllerBridge.

ContentView tiene una propiedad de estado denominada selectedMarker que se inicializa en nil y se actualiza cada vez que se selecciona una ciudad en la lista. Esto se controla mediante buttonAction en la vista CitiesList dentro de ContentView.

ContentView

CitiesList(markers: $markers) { (marker) in
  guard self.selectedMarker != marker else { return }
  self.selectedMarker = marker
  // ...
}

Cada vez que cambie selectedMarker, MapViewControllerBridge debería tener en cuenta este cambio de estado para que pueda crear una animación en el mapa del marcador seleccionado. Por lo tanto, define una nueva vinculación dentro de MapViewControllerBridge del tipo GMSMarker y asigna un nombre a la propiedad selectedMarker.

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  @Binding var selectedMarker: GMSMarker?
}
  1. Actualiza MapViewControllerBridge para animar el mapa cada vez que cambie selectedMarker.

Una vez que se declara una nueva vinculación, debes actualizar la función updateUIViewController_, context) de MapViewControllerBridge para que el mapa tenga la animación del marcador seleccionado. Para ello, copie el siguiente código:

struct MapViewControllerBridge: UIViewControllerRepresentable {
  @Binding var selectedMarker: GMSMarker?

  func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
    markers.forEach { $0.map = uiViewController.map }
    selectedMarker?.map = uiViewController.map
    animateToSelectedMarker(viewController: uiViewController)
  }

  private func animateToSelectedMarker(viewController: MapViewController) {
    guard let selectedMarker = selectedMarker else {
      return
    }

    let map = viewController.map
    if map.selectedMarker != selectedMarker {
      map.selectedMarker = selectedMarker
      DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        map.animate(toZoom: kGMSMinZoomLevel)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
          map.animate(with: GMSCameraUpdate.setTarget(selectedMarker.position))
          DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
            map.animate(toZoom: 12)
          })
        }
      }
    }
  }
}

La función animateToSelectedMarker(viewController) realizará una secuencia de animaciones de mapas con la función animate(with) de GMSMapView.

  1. Pasa el selectedMarker de ContentView a MapViewControllerBridge

Una vez que MapViewControllerBridge tenga la nueva vinculación declarada, actualiza ContentView para pasar el selectedMarker en el que se crea una instancia de MapViewControllerBridge.

ContentView

struct ContentView: View {
  // ...
  var body: some View {
    // ...
    GeometryReader { geometry in
      ZStack(alignment: .top) {
        // Map
        MapViewControllerBridge(markers: $markers, selectedMarker: $selectedMarker)
        // ...
      }
    }
  }
}

Ahora, cuando completes este paso, se creará una animación en el mapa cada vez que se seleccione una ciudad nueva en la lista.

Crea una animación en la vista de SwiftUI para destacar la ciudad

SwiftUI facilita mucho la animación de vistas, ya que se encarga de realizar las animaciones de transiciones de estado. Para demostrar esto, agregarás más animaciones enfocando la vista a la ciudad seleccionada después de que se complete la animación en el mapa. Completa los siguientes pasos:

  1. Agrega un cierre de onAnimationEnded a MapViewControllerBridge.

Debido a que la animación de SwiftUI se realizará después de la secuencia de animación en el mapa que agregaste anteriormente, declara un nuevo cierre con el nombre onAnimationEnded dentro de MapViewControllerBridge y, luego, invoca este cierre después de una demora de 0.5 segundos posterior a la última animación en el mapa dentro del método animateToSelectedMarker(viewController).

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
    var onAnimationEnded: () -> ()

    private func animateToSelectedMarker(viewController: MapViewController) {
    guard let selectedMarker = selectedMarker else {
      return
    }

    let map = viewController.map
    if map.selectedMarker != selectedMarker {
      map.selectedMarker = selectedMarker
      DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
        map.animate(toZoom: kGMSMinZoomLevel)
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
          map.animate(with: GMSCameraUpdate.setTarget(selectedMarker.position))
          DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
            map.animate(toZoom: 12)
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
              // Invoke onAnimationEnded() once the animation sequence completes
              onAnimationEnded()
            })
          })
        }
      }
    }
  }
}
  1. Implementa onAnimationEnded en MapViewControllerBridge.

Implementa el cierre onAnimationEnded en el que se crean instancias de MapViewControllerBridge dentro de ContentView. Copia y pega el siguiente código, que agrega un nuevo estado denominado zoomInCenter, modifica la vista mediante clipShape y, además, varía el diámetro de la forma recortada según el valor de zoomInCenter.

ContentView

struct ContentView: View {
  @State var zoomInCenter: Bool = false
  // ...
  var body: some View {
    // ...
    GeometryReader { geometry in
      ZStack(alignment: .top) {
        // Map
        let diameter = zoomInCenter ? geometry.size.width : (geometry.size.height * 2)
        MapViewControllerBridge(markers: $markers, selectedMarker: $selectedMarker, onAnimationEnded: {
          self.zoomInCenter = true
        })
        .clipShape(
           Circle()
             .size(
               width: diameter,
               height: diameter
             )
             .offset(
               CGPoint(
                 x: (geometry.size.width - diameter) / 2,
                 y: (geometry.size.height - diameter) / 2
               )
             )
        )
        .animation(.easeIn)
        .background(Color(red: 254.0/255.0, green: 1, blue: 220.0/255.0))
      }
    }
  }
}
  1. Ejecuta la app para ver las animaciones.

9. Envía un evento a SwiftUI

En este paso, escucharás eventos emitidos desde GMSMapView y los enviarás a SwiftUI. Específicamente, establecerás un delegado en la vista de mapa y escucharás los eventos de movimiento de la cámara, de modo que, cuando una ciudad se enfoque y la cámara del mapa se mueva por un gesto, esta vista perderá el enfoque para que puedas ver más partes del mapa.

Cómo usar los coordinadores de SwiftUI

GMSMapView emite eventos como cuando se cambia la posición de la cámara o cuando se presiona un marcador. El mecanismo para escuchar estos eventos funciona con el protocolo GMSMapViewDelegate. SwiftUI presenta el concepto de un coordinador que actúa específicamente como el delegado de los controladores de vista de UIKit. Por lo tanto, en el mundo de SwiftUI, el coordinador debe encargarse de cumplir con el protocolo GMSMapViewDelegate. Para hacerlo, sigue los pasos que se indican a continuación:

  1. Crea un coordinador con el nombre MapViewCoordinator dentro de MapViewControllerBridge.

Crea una clase anidada dentro de la clase MapViewControllerBridge y llámala MapViewCoordinator. Esta clase debe cumplir con GMSMapViewDelegate y declarar MapViewControllerBridge como una propiedad.

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
    var mapViewControllerBridge: MapViewControllerBridge

    init(_ mapViewControllerBridge: MapViewControllerBridge) {
      self.mapViewControllerBridge = mapViewControllerBridge
    }
  }
}
  1. Implementa makeCoordinator() en MapViewControllerBridge.

A continuación, implementa el método makeCoordinator() dentro de MapViewControllerBridge y muestra una instancia del MapViewCoodinator que creaste en el paso anterior.

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  func makeCoordinator() -> MapViewCoordinator {
    return MapViewCoordinator(self)
  }
}
  1. Configura MapViewCoordinator como el delegado de la vista de mapa.

Después de crear el coordinador personalizado, el próximo paso es establecerlo como el delegado de la vista de mapa del controlador de vista. Para ello, actualiza la inicialización del controlador de vista en makeUIViewController(context). Se podrá acceder al coordinador creado en el paso anterior desde el objeto Context.

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  func makeUIViewController(context: Context) -> MapViewController {
    let uiViewController = MapViewController()
    uiViewController.map.delegate = context.coordinator
    return uiViewController
  }
  1. Agrega un cierre en MapViewControllerBridge para que el evento de movimiento de la cámara se propague hacia arriba.

Dado que el objetivo es actualizar la vista con los movimientos de la cámara, declara una nueva propiedad de cierre que acepte un valor booleano dentro de MapViewControllerBridge con el nombre mapViewWillMove y, luego, invoca este cierre en el método delegado mapView(_, willMove) dentro de MapViewCoordinator. Pasa el valor de gesture al cierre, de modo que la vista de SwiftUI pueda reaccionar solo a los eventos de movimiento de la cámara relacionados con gestos.

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  var mapViewWillMove: (Bool) -> ()
  //...

  final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
    // ...
    func mapView(_ mapView: GMSMapView, willMove gesture: Bool) {
      self.mapViewControllerBridge.mapViewWillMove(gesture)
    }
  }
}
  1. Actualiza ContentView para pasar un valor de mapWillMove.

Con el nuevo cierre declarado en MapViewControllerBridge, actualiza ContentView a fin de pasar un valor para este nuevo cierre. Dentro de ese cierre, cambia el estado de zoomInCenter a false si el evento de movimiento está relacionado con un gesto. De este modo, el mapa volverá a verse completamente cuando se mueva con un gesto.

ContentView

struct ContentView: View {
  @State var zoomInCenter: Bool = false
  // ...
  var body: some View {
    // ...
    GeometryReader { geometry in
      ZStack(alignment: .top) {
        // Map
        let diameter = zoomInCenter ? geometry.size.width : (geometry.size.height * 2)
        MapViewControllerBridge(markers: $markers, selectedMarker: $selectedMarker, onAnimationEnded: {
          self.zoomInCenter = true
        }, mapViewWillMove: { (isGesture) in
          guard isGesture else { return }
          self.zoomInCenter = false
        })
        // ...
      }
    }
  }
}
  1. Ejecuta la app para ver los nuevos cambios.

10. Felicitaciones

Felicitaciones por haber llegado hasta aquí. Abarcaste una gran cantidad de temas y esperamos que las lecciones que aprendiste te permitan compilar tu propia app de SwiftUI mediante el SDK de Maps para iOS.

Lo que aprendiste

¿Qué sigue?

  • SDK de Maps para iOS: Documentación oficial
  • SDK de Places para iOS: Cómo encontrar empresas locales y lugares de interés cerca de tu ubicación
  • maps-sdk-for-ios-samples: Código de muestra en GitHub que incluye todas las funciones dentro del SDK de Maps para iOS
  • SwiftUI: Documentación oficial de Apple
  • Responde esta pregunta para ayudarnos a crear el contenido que te resultaría más útil:

¿Qué otros codelabs te gustaría ver?

Visualización de datos en mapas Más información sobre cómo personalizar el estilo de mis mapas Creación de interacciones 3D en los mapas

¿El codelab que quieres no figura arriba? Crea un nuevo problema aquí para solicitarlo.