Aggiungere una mappa alla tua app per iOS con SwiftUI (Swift)

1. Prima di iniziare

Questo codelab ti insegna a utilizzare Maps SDK for iOS con SwiftUI.

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

Prerequisiti

  • Conoscenza di base di Swift
  • Familiarità di base con SwiftUI

In questo lab proverai a:

  • Attiva e utilizza Maps SDK for iOS per aggiungere Google Maps a un'app per iOS utilizzando SwiftUI.
  • Aggiungi indicatori alla mappa.
  • Passare lo stato tra un oggetto SwiftUI e un oggetto GMSMapView.

Che cosa ti serve

2. Configurazione

Per il seguente passaggio di attivazione, abilita Maps SDK for iOS.

Configurare Google Maps Platform

Se non hai ancora un account Google Cloud Platform e un progetto con la fatturazione abilitata, consulta la guida Guida introduttiva 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 in 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 i passaggi descritti in questo video o in questa documentazione. Tutte le richieste a Google Maps Platform richiedono una chiave API.

3. Scarica il codice di avvio

Per iniziare il più rapidamente possibile, ecco un codice iniziale che ti aiuterà a seguire questo codelab. Puoi passare direttamente alla soluzione, ma se vuoi seguire tutti i passaggi per crearla da solo, continua a leggere.

  1. Clona il repository se hai installato git.
git clone https://github.com/googlecodelabs/maps-ios-swiftui.git

In alternativa, puoi fare clic sul seguente pulsante per scaricare il codice sorgente.

  1. Una volta ricevuto il codice, in un terminale cd nella directory starter/GoogleMapsSwiftUI.
  2. Esegui carthage update --platform iOS per scaricare Maps SDK for iOS
  3. Infine, apri il file GoogleMapsSwiftUI.xcodeproj in Xcode.

4. Panoramica del codice

Nel progetto iniziale che hai scaricato, sono state fornite e implementate le seguenti classi:

  • AppDelegate: il UIApplicationDelegate dell'applicazione. È qui che verrà inizializzato Maps SDK for iOS.
  • City: una struttura che rappresenta una città (contiene un nome e le coordinate della città).
  • MapViewController: un UIKit con ambito ridotto UIViewController contenente una mappa di Google (GMSMapView)
    • SceneDelegate: l'UIWindowSceneDelegate dell'applicazione da cui viene istanziato ContentView.

Inoltre, le seguenti classi hanno implementazioni parziali e verranno completate da te entro la fine di questo codelab:

  • ContentView: la visualizzazione SwiftUI di primo livello che contiene la tua app.
  • MapViewControllerBridge: una classe che collega una visualizzazione UIKit a una visualizzazione SwiftUI. Nello specifico, questa è la classe che renderà MapViewController accessibile in SwiftUI.

5. SwiftUI e UIKit

SwiftUI è stato introdotto in iOS 13 come framework UI alternativo a UIKit per lo sviluppo di applicazioni per iOS. Rispetto al suo predecessore UIKit, SwiftUI offre una serie di vantaggi. Per citarne alcuni:

  • Le visualizzazioni si aggiornano automaticamente quando lo stato cambia. Utilizzando oggetti chiamati State, qualsiasi modifica al valore sottostante che contiene causerà l'aggiornamento automatico dell'interfaccia utente.
  • Le anteprime live consentono uno sviluppo più rapido. Le anteprime live riducono al minimo la necessità di creare e implementare codice in un emulatore per visualizzare le modifiche visive, in quanto l'anteprima della visualizzazione SwiftUI è facilmente visibile in Xcode.
  • La fonte di riferimento è in Swift. Tutte le viste in SwiftUI sono dichiarate in Swift, quindi non è più necessario utilizzare Interface Builder.
  • Interopera con UIKit. L'interoperabilità con UIKit garantisce che le app esistenti possano utilizzare SwiftUI in modo incrementale con le visualizzazioni esistenti. Inoltre, le librerie che non supportano ancora SwiftUI, come l'SDK Maps per iOS, possono comunque essere utilizzate in SwiftUI.

Ci sono anche alcuni svantaggi:

  • SwiftUI è disponibile solo su iOS 13 o versioni successive.
  • La gerarchia delle visualizzazioni non può essere esaminata nelle anteprime di Xcode.

Stato e flusso di dati di SwiftUI

SwiftUI offre un nuovo modo di creare UI utilizzando un approccio dichiarativo: indichi a SwiftUI l'aspetto che vuoi che abbia la tua visualizzazione insieme a tutti i diversi stati, e il sistema farà il resto. SwiftUI gestisce l'aggiornamento della visualizzazione ogni volta che lo stato sottostante cambia a causa di un evento o di un'azione dell'utente. Questo design è comunemente chiamato flusso di dati unidirezionale. Sebbene i dettagli di questa progettazione non rientrino nell'ambito di questo codelab, ti consigliamo di leggere come funziona nella documentazione di Apple su State and Data Flow.

Eseguire il bridging di UIKit e SwiftUI utilizzando UIViewRepresentable o UIViewControllerRepresentable

Poiché l'SDK Maps per iOS è basato su UIKit e non fornisce una visualizzazione compatibile con SwiftUI, il suo utilizzo in SwiftUI richiede la conformità a UIViewRepresentable o UIViewControllerRepresentable. Questi protocolli consentono a SwiftUI di includere rispettivamente UIView e UIViewController creati con UIKit. Anche se puoi utilizzare uno dei due protocolli per aggiungere una mappa di Google a una visualizzazione SwiftUI, nel passaggio successivo vedremo come utilizzare un UIViewControllerRepresentable per includere un UIViewController contenente una mappa.

6. Aggiungere una mappa

In questa sezione aggiungerai Google Maps a una visualizzazione SwiftUI.

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

Aggiungere la chiave API

La chiave API creata in un passaggio precedente deve essere fornita a Maps SDK for iOS per associare il tuo account alla mappa che verrà visualizzata nell'app.

Per fornire la chiave API, apri il file AppDelegate.swift e vai al metodo application(_, didFinishLaunchingWithOptions). L'SDK viene inizializzato utilizzando GMSServices.provideAPIKey() con la stringa "YOUR_API_KEY". Sostituisci questa stringa con la tua chiave API. Il completamento di questo passaggio inizializzerà l'SDK Maps per iOS all'avvio dell'applicazione.

Aggiungere una mappa Google utilizzando MapViewControllerBridge

Ora che la chiave API viene fornita all'SDK, il passaggio successivo consiste nel visualizzare la mappa nell'app.

Il controller di visualizzazione fornito nel codice iniziale, MapViewController, contiene un GMSMapView nella visualizzazione. Tuttavia, poiché questo controller di visualizzazione è stato creato in UIKit, dovrai eseguire il bridging di questa classe in SwiftUI in modo che possa essere utilizzata all'interno di ContentView. Per farlo:

  1. Apri il file MapViewControllerBridge in Xcode.

Questa classe è conforme a UIViewControllerRepresentable, il protocollo necessario per eseguire il wrapping di un UIViewController UIKit in modo che possa essere utilizzato come visualizzazione SwiftUI. In altre parole, l'adeguamento a questo protocollo facilita il bridging di una vista UIKit a una vista SwiftUI. Per rispettare questo protocollo è necessario implementare due metodi:

  • makeUIViewController(context): questo metodo viene chiamato da SwiftUI per creare l'UIViewController sottostante. È qui che istanzierai UIViewController e gli passerai il suo stato iniziale.
  • updateUIViewController(_, context): questo metodo viene chiamato da SwiftUI ogni volta che lo stato cambia. È qui che apporti le modifiche all'intent UIViewController sottostante per reagire alla modifica dello stato.
  1. Crea un MapViewController

All'interno della funzione makeUIViewController(context), crea un'istanza di un nuovo MapViewController e restituiscila come risultato. Dopo averlo fatto, il tuo MapViewControllerBridge dovrebbe avere il seguente aspetto:

MapViewControllerBridge

import GoogleMaps
import SwiftUI

struct MapViewControllerBridge: UIViewControllerRepresentable {

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

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

Utilizzare MapViewControllerBridge in ContentView

Ora che MapViewControllerBridge sta creando un'istanza di MapViewController, il passaggio successivo consiste nell'utilizzare questa struct all'interno di ContentView per visualizzare una mappa.

  1. Apri il file ContentView in Xcode.

ContentView viene istanziato in SceneDelegate e contiene la visualizzazione dell'applicazione di primo livello. La mappa verrà aggiunta da questo file.

  1. Crea un MapViewControllerBridge all'interno della proprietà body.

All'interno della proprietà body di questo file, è già stato fornito e implementato un ZStack. ZStack contiene un elenco interattivo e trascinabile di città che utilizzerai in un passaggio successivo. Per il momento, all'interno di ZStack crea un MapViewControllerBridge come prima visualizzazione figlio di ZStack in modo che venga visualizzata una mappa nell'app dietro la visualizzazione dell'elenco delle città. In questo modo, i contenuti della proprietà body all'interno di ContentView dovrebbero essere simili a questi:

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. Ora esegui l'app. Dovresti vedere la mappa caricata sullo schermo del dispositivo insieme a un elenco trascinabile di città nella parte inferiore dello schermo.

7. Aggiungere indicatori alla mappa

Nel passaggio precedente, hai aggiunto una mappa insieme a un elenco interattivo che mostra un elenco di città. In questa sezione aggiungerai indicatori per ogni città dell'elenco.

map-with-markers@2x.png

Marcatori come stato

ContentView dichiara una proprietà chiamata markers, che è un elenco di GMSMarker che rappresentano ogni città dichiarata nella proprietà statica cities. Tieni presente che questa proprietà è annotata con il wrapper di proprietà SwiftUI State per indicare che deve essere gestita da SwiftUI. Pertanto, se vengono rilevate modifiche a questa proprietà, ad esempio l'aggiunta o la rimozione di un indicatore, le visualizzazioni che utilizzano questo stato verranno aggiornate.

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
  }

Tieni presente che ContentView utilizza la proprietà markers per eseguire il rendering dell'elenco delle città passandola alla classe 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)
      }
    }
  }
}

Passa lo stato a MapViewControllerBridge utilizzando @Binding

Oltre all'elenco delle città che mostrano i dati della proprietà markers, passa questa proprietà alla struttura MapViewControllerBridge in modo che possa essere utilizzata per visualizzare i relativi indicatori sulla mappa. Per farlo, segui questi passaggi:

  1. Dichiara una nuova proprietà markers all'interno di MapViewControllerBridge annotata con @Binding

MapViewControllerBridge

struct MapViewControllerBridge: : UIViewControllerRepresentable {
  @Binding var markers: [GMSMarker]
  // ...
}
  1. In MapViewControllerBridge, aggiorna il metodo updateUIViewController(_, context) per utilizzare la proprietà markers

Come accennato nel passaggio precedente, updateUIViewController(_, context) verrà chiamato da SwiftUI ogni volta che lo stato cambia. È all'interno di questo metodo che vogliamo aggiornare la mappa in modo da visualizzare i marcatori in markers. Per farlo, devi aggiornare la proprietà map di ogni indicatore. Dopo aver completato questo passaggio, il tuo MapViewControllerBridge dovrebbe avere il seguente aspetto:

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. Passa la proprietà markers da ContentView a MapViewControllerBridge

Poiché hai aggiunto una nuova proprietà in MapViewControllerBridge, ora è necessario che il valore di questa proprietà venga passato nell'inizializzatore per MapViewControllerBridge. Pertanto, se provi a creare l'app, noterai che non verrà compilata. Per risolvere il problema, aggiorna ContentView in cui viene creato MapViewControllerBridge e inserisci la proprietà markers nel seguente modo:

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

Nota che il prefisso $ è stato utilizzato per passare markers a MapViewControllerBridge, poiché prevede una proprietà associata. $ è un prefisso riservato per l'utilizzo con i wrapper delle proprietà Swift. Se applicato a uno stato, restituisce un Binding.

  1. Esegui l'app per visualizzare i segnaposto sulla mappa.

8. Animare fino a una città selezionata

Nel passaggio precedente, hai aggiunto indicatori a una mappa passando lo stato da una visualizzazione SwiftUI all'altra. In questo passaggio, animerai una città o un indicatore dopo che è stato toccato nell'elenco interattivo. Per eseguire l'animazione, reagisci alle modifiche a uno stato modificando la posizione della videocamera della mappa quando si verifica la modifica. Per scoprire di più sul concetto di videocamera della mappa, consulta Videocamera e visualizzazione.

animate-city@2x.png

Animare la mappa sulla città selezionata

Per animare la mappa in modo che mostri una città selezionata:

  1. Definisci una nuova associazione in MapViewControllerBridge

ContentView ha una proprietà State chiamata selectedMarker che viene inizializzata su nil e viene aggiornata ogni volta che viene selezionata una città nell'elenco. Questa operazione viene gestita dalla visualizzazione CitiesList buttonAction all'interno di ContentView.

ContentView

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

Ogni volta che selectedMarker cambia, MapViewControllerBridge deve essere a conoscenza di questa modifica dello stato in modo da poter animare la mappa fino al marker selezionato. Definisci quindi un nuovo Binding all'interno di MapViewControllerBridge di tipo GMSMarker e assegna alla proprietà il nome selectedMarker.

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  @Binding var selectedMarker: GMSMarker?
}
  1. Aggiorna MapViewControllerBridge per animare la mappa ogni volta che selectedMarker cambia

Una volta dichiarato un nuovo Binding, devi aggiornare la funzione MapViewControllerBridge di updateUIViewController_, context) in modo che la mappa si animi fino al marker selezionato. Per farlo, copia il seguente codice:

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 funzione animateToSelectedMarker(viewController) eseguirà una sequenza di animazioni della mappa utilizzando la funzione animate(with) di GMSMapView.

  1. Pass ContentView's selectedMarker to MapViewControllerBridge

Una volta che MapViewControllerBridge ha dichiarato il nuovo Binding, aggiorna ContentView per passare a selectedMarker dove viene istanziato MapViewControllerBridge.

ContentView

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

Il completamento di questo passaggio ora anima la mappa ogni volta che viene selezionata una nuova città nell'elenco.

Animare la visualizzazione SwiftUI per mettere in evidenza la città

SwiftUI semplifica il processo di animazione delle visualizzazioni, in quanto gestisce l'esecuzione delle animazioni per le transizioni di stato. Per dimostrarlo, aggiungerai altre animazioni concentrando la visualizzazione sulla città selezionata al termine dell'animazione della mappa. Per farlo, segui questi passaggi:

  1. Aggiungere una chiusura onAnimationEnded a MapViewControllerBridge

Poiché l'animazione SwiftUI verrà eseguita dopo la sequenza di animazione della mappa aggiunta in precedenza, dichiara una nuova chiusura chiamata onAnimationEnded all'interno di MapViewControllerBridge e richiama questa chiusura dopo un ritardo di 0,5 secondi dopo l'ultima animazione della mappa all'interno del metodo 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 in MapViewControllerBridge

Implementa la chiusura onAnimationEnded in cui MapViewControllerBridge viene istanziato all'interno di ContentView. Copia e incolla il seguente codice che aggiunge un nuovo stato chiamato zoomInCenter e modifica anche la visualizzazione utilizzando clipShape e varia il diametro della forma ritagliata a seconda del valore di 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. Esegui l'app per vedere le animazioni.

9. Invia un evento a SwiftUI

In questo passaggio, ascolterai gli eventi emessi da GMSMapView e li invierai a SwiftUI. In particolare, imposterai un delegato per la visualizzazione mappa e ascolterai gli eventi di spostamento della videocamera in modo che, quando una città è messa a fuoco e la videocamera della mappa si sposta da un gesto, la visualizzazione mappa si sfoca per consentirti di vedere più dettagli della mappa.

Utilizzare i coordinatori SwiftUI

GMSMapView genera eventi come modifiche alla posizione della videocamera o quando viene toccato un indicatore. Il meccanismo per l'ascolto di questi eventi avviene tramite il protocollo GMSMapViewDelegate. SwiftUI introduce il concetto di un coordinatore, che viene utilizzato specificamente per fungere da delegato per i controller di visualizzazione UIKit. Pertanto, nel mondo di SwiftUI, un coordinatore deve occuparsi di rispettare il protocollo GMSMapViewDelegate. Per farlo, segui questi passaggi:

  1. Crea un coordinatore denominato MapViewCoordinator all'interno di MapViewControllerBridge

Crea una classe nidificata all'interno della classe MapViewControllerBridge e chiamala MapViewCoordinator. Questa classe deve essere conforme a GMSMapViewDelegate e deve dichiarare MapViewControllerBridge come proprietà.

MapViewControllerBridge

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

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

Successivamente, implementa il metodo makeCoordinator() all'interno di MapViewControllerBridge e restituisci un'istanza di MapViewCoodinator che hai creato nel passaggio precedente.

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  func makeCoordinator() -> MapViewCoordinator {
    return MapViewCoordinator(self)
  }
}
  1. Imposta MapViewCoordinator come delegato della visualizzazione mappa

Una volta creato il coordinatore personalizzato, il passaggio successivo consiste nell'impostarlo come delegato per la visualizzazione della mappa del controller di visualizzazione. Per farlo, aggiorna l'inizializzazione del controller di visualizzazione in makeUIViewController(context). Il coordinatore creato nel passaggio precedente sarà accessibile dall'oggetto Context.

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  func makeUIViewController(context: Context) -> MapViewController {
    let uiViewController = MapViewController()
    uiViewController.map.delegate = context.coordinator
    return uiViewController
  }
}
  1. Aggiungi una chiusura a MapViewControllerBridge in modo che l'evento di movimento della videocamera possa essere propagato verso l'alto

Poiché l'obiettivo è aggiornare la visualizzazione con i movimenti della videocamera, dichiara una nuova proprietà di chiusura che accetta un valore booleano all'interno di MapViewControllerBridge chiamata mapViewWillMove e richiama questa chiusura nel metodo delegato mapView(_, willMove) all'interno di MapViewCoordinator. Passa il valore di gesture alla chiusura in modo che la visualizzazione SwiftUI possa reagire solo agli eventi di movimento della videocamera correlati ai gesti.

MapViewControllerBridge

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

  final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
    // ...
    func mapView(_ mapView: GMSMapView, willMove gesture: Bool) {
      self.mapViewControllerBridge.mapViewWillMove(gesture)
    }
  }
}
  1. Aggiorna ContentView per passare un valore per mapWillMove

Con la nuova chiusura dichiarata il giorno MapViewControllerBridge, aggiorna ContentView per inserire un valore per questa nuova chiusura. All'interno della chiusura, imposta lo stato zoomInCenter su false se l'evento di movimento è correlato a un gesto. In questo modo, la mappa viene visualizzata di nuovo a schermo intero quando viene spostata 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. Esegui l'app per vedere le nuove modifiche.

10. Complimenti

Congratulazioni per aver raggiunto questo traguardo. Hai fatto molta strada e ci auguriamo che le lezioni che hai imparato ti permettano ora di creare la tua app SwiftUI utilizzando Maps SDK for iOS.

Che cosa hai imparato

  • Differenze tra SwiftUI e UIKit
  • Come creare un ponte tra SwiftUI e UIKit utilizzando UIViewControllerRepresentable
  • Come apportare modifiche alla visualizzazione della mappa con State e Binding
  • Come inviare un evento dalla visualizzazione della mappa a SwiftUI utilizzando un coordinatore

Passaggi successivi

  • Maps SDK for iOS
    • documentazione ufficiale di Maps SDK for iOS
  • Places SDK for iOS: trova attività locali e punti d'interesse nelle vicinanze
  • maps-sdk-for-ios-samples
    • codice di esempio su GitHub che mostra tutte le funzionalità dell'SDK Maps per iOS.
  • SwiftUI: documentazione ufficiale di Apple su SwiftUI
  • 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 edifici per interazioni 3D nelle mappe

Non riesci a trovare il codelab che ti interessa di più? Richiedilo con un nuovo problema qui.