Dodawanie mapy do aplikacji na iOS za pomocą SwiftUI (Swift)

1. Zanim zaczniesz

Dzięki nim dowiesz się, jak używać pakietu SDK Maps na iOS w połączeniu ze SwiftUI.

zrzut ekranu-iPhone'a-12-czarny@2x.png

Wymagania wstępne

  • Podstawowe informacje o Swift
  • Podstawowe informacje o SwiftUI

Jakie zadania wykonasz:

  • Włącz pakiet SDK Maps na iOS i używaj go, aby dodawać Mapy Google do aplikacji za pomocą SwiftUI.
  • Dodaj znaczniki na mapie.
  • Przekazanie stanu z widoku SwiftUI do obiektu GMSMapView i odwrotnie.

Czego potrzebujesz

2. Konfiguracja

W następnym kroku włącz Pakiet SDK Map Google na iOS.

Konfigurowanie Google Maps Platform

Jeśli nie masz jeszcze konta Google Cloud Platform ani projektu z włączonymi płatnościami, przeczytaj przewodnik Pierwsze kroki z Google Maps Platform, by utworzyć konto rozliczeniowe i projekt.

  1. W Cloud Console kliknij menu projektu i wybierz projekt, którego chcesz użyć w tym ćwiczeniu z programowania.

  1. Włącz interfejsy API i pakiety SDK Google Maps Platform wymagane w ramach tego ćwiczenia z ćwiczeń w Google Cloud Marketplace. W tym celu wykonaj czynności opisane w tym filmie lub w tej dokumentacji.
  2. Wygeneruj klucz interfejsu API na stronie Dane logowania w Cloud Console. Odpowiednie instrukcje znajdziesz w tym filmie lub w tej dokumentacji. Wszystkie żądania wysyłane do Google Maps Platform wymagają klucza interfejsu API.

3. Pobierz kod startowy

Aby ułatwić Ci rozpoczęcie tego ćwiczenia, skorzystaj z tego kodu, który ułatwia rozpoczęcie ćwiczeń z programowania. Zachęcamy do przejścia do rozwiązania, ale jeśli chcesz samodzielnie wykonać wszystkie kroki niezbędne do jego zbudowania, czytaj dalej.

  1. Skopiuj repozytorium, jeśli masz zainstalowane narzędzie git.
git clone https://github.com/googlecodelabs/maps-ios-swiftui.git

Możesz też kliknąć poniższy przycisk, aby pobrać kod źródłowy.

  1. Po otrzymaniu kodu przejdź do terminala cd do terminala starter/GoogleMapsSwiftUI.
  2. Uruchom aplikację carthage update --platform iOS, by pobrać pakiet SDK Maps na iOS
  3. Na koniec otwórz plik GoogleMapsSwiftUI.xcodeproj w Xcode.

4. Przegląd kodu

W pobranym przez Ciebie projekcie startowym dodaliśmy i wdrożyliśmy te klasy:

  • AppDelegate – aplikacja UIApplicationDelegate. W tym miejscu zostanie zainicjowany pakiet SDK Maps na iOS.
  • City – budowla reprezentująca miasto (zawiera nazwę i współrzędne miasta).
  • MapViewController – prosty UIKit UIViewController zawierający mapę Google (GMSMapView).
  • SceneDelegate - aplikacja UIWindowSceneDelegate, z której utworzono ContentView.

Te zajęcia mają częściowe implementacje i ukończysz je do końca tego ćwiczenia z programowania:

  • ContentView – widok aplikacji SwiftUI najwyższego poziomu zawierający Twoją aplikację.
  • MapViewControllerBridge – klasa łącząca widok UIKit z widokiem SwiftUI. Chodzi w nich o udostępnienie MapViewController w narzędziu SwiftUI.

5. Używanie SwiftUI a UIKit

SwiftUI został wprowadzony w iOS 13 jako alternatywna platforma interfejsu UIKit do programowania aplikacji na iOS. W porównaniu z poprzednim interfejsem UIKit SwiftUI ma wiele zalet. Oto kilka przykładów:

  • Widoki są aktualizowane automatycznie po zmianie stanu. Używanie obiektu o nazwie State w celu wprowadzenia zmian w znajdującej się w nim wartości spowoduje automatyczne zaktualizowanie interfejsu.
  • Podgląd na żywo umożliwia szybszy rozwój. Podgląd na żywo minimalizuje kompilację i wdrażanie kodu w emulatorze, aby można było zobaczyć zmiany wizualne, ponieważ podgląd widoku SwiftUI jest łatwo widoczny w Xcode.
  • Podstawowym źródłem danych jest Swift. Wszystkie widoki w SwiftUI są deklarowane w Swift, więc używanie interfejsu Builder API nie jest już potrzebne.
  • Współdziała z UIKit. Dzięki współdziałaniu z UIKit istniejące aplikacje mogą stopniowo używać SwiftUI z istniejącymi widokami. Oprócz tego biblioteki, które nie obsługują jeszcze SwiftUI, takie jak Maps SDK na iOS, mogą nadal być używane w SwiftUI.

Są też pewne wady:

  • SwiftUI jest dostępna tylko na urządzeniach z systemem iOS 13 lub nowszym.
  • Hierarchii widoku nie można analizować w podglądach Xcode.

Stan SwiftUI i przepływ danych

SwiftUI oferuje nowatorską metodę tworzenia UI za pomocą deklaratywnego podejścia. Powiedz SwiftUI, jak ma wyglądać Twój widok wraz z różnymi stanami. System zajmie się resztą. SwiftUI obsługuje aktualizowanie widoku w razie zmiany stanu z powodu zdarzenia lub działania użytkownika. Ten projekt jest często nazywany jednokierunkowym przepływem danych. Chociaż szczegóły dotyczące tego projektu nie należą do zakresu tych ćwiczeń z programowania, zalecamy zapoznanie się z dokumentacją State and Data Flow firmy Apple.

Łączenie UIKit i SwiftUI za pomocą UIViewRepresentable lub UIViewControllerRepresentable

Maps SDK na iOS został oparty na UIKit i nie zapewnia jeszcze widoku zgodnego ze SwiftUI. Dlatego używanie go w SwiftUI wymaga spełnienia wymagań UIViewRepresentable lub UIViewControllerRepresentable. Te protokoły powodują, że SwiftUI zawiera odpowiednio elementy UIView i UIViewController utworzone przez UIKit. Za pomocą dowolnego protokołu możesz dodać mapę Google do widoku SwiftUI, ale w następnym kroku skorzystamy z obiektu UIViewControllerRepresentable, by dodać UIViewController do mapy.

6. Dodawanie mapy

W tej sekcji dodasz Mapy Google do widoku SwiftUI.

dodaj-mapa-zrzutu ekranu@2x.png

Dodaj klucz interfejsu API

Klucz interfejsu API utworzony we wcześniejszym kroku należy przekazać do pakietu Maps SDK na iOS, aby można było powiązać konto z mapą, która będzie wyświetlana w aplikacji.

Aby podać klucz interfejsu API, otwórz plik AppDelegate.swift i przejdź do metody application(_, didFinishLaunchingWithOptions). Obecnie pakiet SDK jest inicjowany przez GMSServices.provideAPIKey() z ciągiem "YOUR_API_KEY". Zastąp ten ciąg swoim kluczem interfejsu API. Ten krok spowoduje zainicjowanie pakietu Maps SDK na iOS po uruchomieniu aplikacji.

Dodawanie mapy za pomocą obiektu MapViewControllerBridge

Po udostępnieniu klucza interfejsu API możesz przesłać mapę w aplikacji.

Kontroler widoku dostępny w kodzie początkowym (MapViewController) zawiera obecnie GMSMapView w widoku. Ponieważ ten kontroler widoku został utworzony w UIKit, musisz połączyć tę klasę z SwiftUI, aby można było jej używać w: ContentView. Aby to zrobić:

  1. Otwórz plik MapViewControllerBridge w Xcode.

Ta klasa jest zgodna z protokołem UIViewControllerRepresentable, który jest wymagany do opakowania UIKit UIViewController, aby można było go użyć jako widoku SwiftUI. Inaczej mówiąc, zgodność z tym protokołem umożliwia połączenie widoku UIKit z widokiem SwiftUI. Dostosowanie się do tego protokołu wymaga wdrożenia dwóch metod:

  • makeUIViewController(context) – ta metoda jest wywoływana przez SwiftUI, by utworzyć podstawowy element UIViewController. W tym miejscu utworzysz instancję UIViewController i przejdziesz do stanu początkowego.
  • updateUIViewController(_, context) – ta metoda jest wywoływana przez SwiftUI za każdym razem, gdy zmieni się stan. W tym miejscu możesz wprowadzić zmiany w grupie UIViewController, aby zareagować na zmiany stanu.
  1. Utwórz MapViewController

Utwórz instancję nowego elementu MapViewController w funkcji makeUIViewController(context) i zwróć go w wyniku. Gdy to zrobisz, MapViewControllerBridge powinien wyglądać tak:

Mostek kontrolera widoku mapy

import GoogleMaps
import SwiftUI

struct MapViewControllerBridge: UIViewControllerRepresentable {

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

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

Używanie elementu MapViewControllerBridge w ContentView

Gdy MapViewControllerBridge tworzy wystąpienie MapViewController, następnym krokiem jest użycie tej struktury z usługi ContentView do wyświetlenia mapy.

  1. Otwórz plik ContentView w Xcode.

Wystąpienie ContentView zostało utworzone w SceneDelegate i zawiera widok aplikacji najwyższego poziomu. Mapa zostanie dodana z tego pliku.

  1. Utwórz MapViewControllerBridge w obrębie właściwości body.

Właściwość ZStack została już zawarta we właściwości body tego pliku. ZStack zawiera obecnie interaktywną i przeciąganą listę miast, których użyjesz w kolejnym kroku. Na razie w usłudze ZStack utwórz MapViewControllerBridge jako pierwszy widok podrzędny obiektu ZStack, aby w aplikacji wyświetlić mapę z listy miast. Zawartość usługi body w ContentView powinna wyglądać tak:

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. Teraz uruchom aplikację. Na ekranie urządzenia powinno pojawić się wczytanie mapy oraz u dołu ekranu możesz przeciągać listę miast.

7. Dodawanie znaczników do mapy

W poprzednim kroku dodano mapę wraz z interaktywną listą zawierającą listę miast. W tej sekcji możesz dodać znaczniki do poszczególnych miast na liście.

mapa-ze-znacznikami@2x.png

Znaczniki jako stan

ContentView deklaruje obecnie usługę o nazwie markers, która zawiera listę GMSMarker domen i deklaruje każde miasto zadeklarowane w usłudze cities statycznej. Pamiętaj, że ta właściwość jest opatrzona adnotacją opakowującej właściwości SwiftUI State, wskazując, że powinna ona być zarządzana przez SwiftUI. Jeśli więc zostaną wykryte jakiekolwiek zmiany w tej właściwości, np. dodanie lub usunięcie znacznika, zostaną zaktualizowane widoki korzystające z tego stanu.

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
  }

Zwróć uwagę, że ContentView używa właściwości markers do renderowania listy miast, przekazując ją do klasy CitiesList.

Lista miast

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

Przekazanie stanu do elementu MapViewControllerBridge za pomocą powiązania

Oprócz listy miast wyświetlających dane z właściwości markers przekaż tę właściwość do struktury MapViewControllerBridge, aby można było jej używać do wyświetlania tych znaczników na mapie. Aby go ustawić:

  1. Zadeklaruj nową właściwość markers w: MapViewControllerBridge z adnotacją @Binding

Mostek kontrolera widoku mapy

struct MapViewControllerBridge: : UIViewControllerRepresentable {
  @Binding var markers: [GMSMarker]
  // ...
}
  1. Aby skorzystać z właściwości markers, w usłudze MapViewControllerBridge zaktualizuj metodę updateUIViewController(_, context).

Jak wspomnieliśmy w poprzednim kroku, funkcja SwiftUI będzie wywoływać funkcję updateUIViewController(_, context) po każdej zmianie stanu. Chcemy zaktualizować mapę w ten sposób, więc będziemy wyświetlać znaczniki w tej lokalizacji: markers. Aby to zrobić, musisz zaktualizować właściwość map każdego znacznika. Po wykonaniu tej czynności MapViewControllerBridge powinien wyglądać tak:

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. Przekaż właściwość markers z ContentView do MapViewControllerBridge

Ponieważ dodano nową usługę w MapViewControllerBridge, wymaga ona teraz przekazania jej właściwości inicjatora MapViewControllerBridge. Jeśli więc spróbujesz utworzyć aplikację, zauważysz, że nie skompiluje się. Aby rozwiązać ten problem, zaktualizuj atrybut ContentView, w którym jest tworzony element MapViewControllerBridge, i przekaż go w usłudze markers:

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

Zwróć uwagę, że przedrostek $ został użyty w przekierowaniu markers do MapViewControllerBridge, ponieważ oczekuje on powiązanej usługi. $ to zarezerwowany prefiks do użycia z kodami przypisanymi do usługi Swift. Zastosowanie do stanu powoduje zwrócenie wiązania.

  1. Możesz uruchomić aplikację, aby zobaczyć znaczniki na mapie.

8. Animowanie do wybranego miasta

W poprzednim kroku dodano znaczniki do mapy, przekazując stan z jednego widoku SwiftUI do innego. W tym kroku możesz animować miasto/znacznik po kliknięciu go na liście możliwych do interakcji. Aby wprowadzić animację, należy reagować na zmiany w stanie, modyfikując położenie kamery na mapie. Aby dowiedzieć się więcej o pojęciach związanych z aparatem i mapą, zobacz Aparat i widok.

animacja-miasta@2x.png

Animacja mapy do wybranego miasta

Aby animować mapę wybranego miasta:

  1. Zdefiniuj nowe powiązanie w elemencie MapViewControllerBridge

ContentView ma właściwość stanową selectedMarker zainicjowaną do Nini i jest aktualizowana po każdym wybraniu miasta na liście. Jest on obsługiwany przez widok CitiesList buttonAction w ContentView.

ContentView

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

Za każdym razem, gdy selectedMarker się zmieni, MapViewControllerBridge musi wiedzieć o tej zmianie, by animować mapę względem wybranego znacznika. Zdefiniuj nowe powiązanie w ramach typu MapViewControllerBridge typu GMSMarker i nazwij właściwość selectedMarker.

Mostek kontrolera widoku mapy

struct MapViewControllerBridge: UIViewControllerRepresentable {
  @Binding var selectedMarker: GMSMarker?
}
  1. Zaktualizuj aplikację MapViewControllerBridge, aby animować mapę po każdej zmianie parametru selectedMarker

Po zadeklarowaniu nowego powiązania musisz zaktualizować funkcję updateUIViewController_, context) MapViewControllerBridge i to, by mapa animowała się przy użyciu wybranego znacznika. Możesz to zrobić, kopiując poniższy kod:

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

Funkcja animateToSelectedMarker(viewController) wykona sekwencję animacji mapy za pomocą funkcji animate(with) GMSMapView.

  1. Przekazanie selectedMarkerContentView do: MapViewControllerBridge

Po zadeklarowaniu nowego powiązania w MapViewControllerBridge przejdź dalej i zaktualizuj ContentView, aby przechodziło przez selectedMarker, gdzie utworzono instancję MapViewControllerBridge.

ContentView

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

Osiągnięcie tego kroku spowoduje teraz animowanie mapy po wybraniu nowego miasta na liście.

Animacja widoku SwiftUI podkreślająca miasto

SwiftUI sprawia, że animowanie widoków jest bardzo proste, ponieważ obsługuje on animacje wykonywane podczas przejść stanu. Aby to pokazać, musisz dodać więcej animacji, skupiając się na widoku dla wybranego miasta. Aby to zrobić:

  1. Dodaj zamknięcie onAnimationEnded do MapViewControllerBridge

Animacja SwiftUI będzie wykonywana po wcześniej dodanej sekwencji animacji mapy, więc zadeklaruj nowe zamknięcie o nazwie onAnimationEnded w obrębie MapViewControllerBridge i wywołaj to zamknięcie po 0,5 sekundy od ostatniej animacji mapy w metodzie animateToSelectedMarker(viewController).

Mostek kontrolera widoku mapy

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. Wdróż onAnimationEnded w MapViewControllerBridge

Wdróż zamknięcie onAnimationEnded, w którym utworzono instancję MapViewControllerBridge w ciągu ContentView. Skopiuj następujący kod i wklej go, aby dodać nowy stan o nazwie zoomInCenter. Zmienia też widok, używając clipShape i zmienia średnicę przyciętego kształtu w zależności od wartości 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. Uruchom aplikację, aby zobaczyć animacje.

9. Wysyłanie wydarzenia do SwiftUI

W tym kroku odsłuchasz zdarzenia generowane przez GMSMapView i wyślesz je do SwiftUI. W szczególności wyznaczasz osoby wyznaczone w widoku mapy i wykrywasz zdarzenia przemieszczania się zdjęć. Gdy miasto jest zaznaczone, a kamera kamery przesuwa się z gestu, widok mapy jest nieostry, dzięki czemu możesz zobaczyć więcej mapy.

Korzystanie z koordynatorów SwiftUI

GMSMapView emituje zdarzenia, takie jak zmiana położenia kamery lub kliknięcie znacznika. Mechanizmem nasłuchiwania tych zdarzeń jest protokół GMSMapViewCredential. SwiftUI przedstawia koordynatora, który jest używany jako przedstawiciel kontrolerów widoku UIKit. W świecie SwiftUI koordynator odpowiada za przestrzeganie protokołu GMSMapViewDelegate. Aby to zrobić:

  1. Utwórz koordynatora o nazwie MapViewCoordinator w domenie MapViewControllerBridge

Utwórz zagnieżdżoną klasę w klasie MapViewControllerBridge i nazwij ją MapViewCoordinator. Ta klasa powinna być zgodna z GMSMapViewDelegate i zadeklarować MapViewControllerBridge jako właściwość.

Mostek kontrolera widoku mapy

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

    init(_ mapViewControllerBridge: MapViewControllerBridge) {
      self.mapViewControllerBridge = mapViewControllerBridge
    }
  }
}
  1. Wdróż makeCoordinator() w MapViewControllerBridge

Następnie zaimplementuj metodę makeCoordinator() w MapViewControllerBridge i zwróć wystąpienie MapViewCoodinator, które zostało utworzone w poprzednim kroku.

Mostek kontrolera widoku mapy

struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  func makeCoordinator() -> MapViewCoordinator {
    return MapViewCoordinator(self)
  }
}
  1. Ustaw MapViewCoordinator jako przedstawiciela na mapie

Po utworzeniu niestandardowego koordynatora należy ustawić go jako osobę, której przekazano dostęp do widoku mapy kontrolera widoku. Aby to zrobić, zaktualizuj inicjację kontrolera widoku w: makeUIViewController(context). Utworzony koordynator z poprzedniego kroku będzie dostępny z obiektu Context.

Mostek kontrolera widoku mapy

struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  func makeUIViewController(context: Context) -> MapViewController {
    let uiViewController = MapViewController()
    uiViewController.map.delegate = context.coordinator
    return uiViewController
  }
  1. Dodaj zamknięcie w polu MapViewControllerBridge, aby kamera mogła przenieść wydarzenie w górę

Ponieważ celem jest zaktualizowanie widoku o ruchu kamery, zadeklaruj nową właściwość zamknięcia, która akceptuje wartość logiczną MapViewControllerBridge o nazwie mapViewWillMove, i wywoła to zamknięcie w metodzie przekazywania mapView(_, willMove) w MapViewCoordinator. Przekaż wartość gesture do zamknięcia, aby widok SwiftUI mógł reagować tylko na zdarzenia ruchu kamery związane z gestami.

Mostek kontrolera widoku mapy

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

  final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
    // ...
    func mapView(_ mapView: GMSMapView, willMove gesture: Bool) {
      self.mapViewControllerBridge.mapViewWillMove(gesture)
    }
  }
}
  1. Zaktualizuj element ContentView, by przekazywać wartość mapWillMove

Po zadeklarowaniu nowej daty zamknięcia MapViewControllerBridge zaktualizuj ContentView, by podać wartość tego nowego zamknięcia. W ramach tego zamknięcia ustaw stan zoomInCenter na false, jeśli zdarzenie ruchu jest związane z gestem. Mapa zostanie ponownie wyświetlona w pełnym widoku, gdy użytkownik przesunie ją za pomocą gestu.

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. Możesz uruchomić aplikację, aby zapoznać się ze zmianami.

10. Gratulacje

Gratulujemy! Tak daleko! Świetnie Ci idzie i mam nadzieję, że dzięki zdobytym lekcjom udało Ci się stworzyć własną aplikację SwiftUI za pomocą pakietu SDK Maps na iOS.

Czego się nauczysz

Co dalej?

  • Pakiet SDK Map Google na iOS – oficjalna dokumentacja pakietu SDK Map na iOS
  • Pakiet SDK Miejsc na iOS – znajduj lokalne firmy i ciekawe miejsca w pobliżu
  • maps-sdk-for-ios-samples – przykładowy kod z GitHuba, który pokazuje wszystkie funkcje pakietu Maps SDK na iOS.
  • SwiftUI – oficjalna dokumentacja firmy S&ft firmy Apple
  • Pomóż nam utworzyć treści, które będą dla Ciebie najbardziej przydatne, odpowiadając na poniższe pytanie:

Jakie inne ćwiczenia z programowania chcesz obejrzeć?

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

Czy ćwiczeń z programowania nie ma na liście powyżej? Tutaj możesz poprosić o nowy problem.