הוספת מפה לאפליקציית iOS באמצעות SwiftUI (Swift)

קל לארגן דפים בעזרת אוספים אפשר לשמור ולסווג תוכן על סמך ההעדפות שלך.

1. לפני שתתחיל

ב-Codelab הזה אתם יכולים ללמוד איך להשתמש ב-SDK של מפות Google ל-iOS עם SwiftUI.

צילום-iphone-12-black@2x.png

דרישות מוקדמות

  • בסיסי של Swift
  • היכרות בסיסית עם SwiftUI

הפעולות שתבצעו:

  • יש להפעיל את ה-SDK של מפות Google ל-iOS ולהשתמש בו כדי להוסיף את מפות Google לאפליקציה ל-iOS באמצעות SwiftUI.
  • הוספת סמנים למפה.
  • אפשר להעביר מצב מתצוגת SwiftUI לאובייקט GMSMapView ולהפך.

מה תצטרך להכין

2. להגדרה

בשלב הבא של ההפעלה, מפעילים את SDK של מפות Google ל-iOS.

הגדרת מפות Google

אם עדיין אין לכם חשבון Google Cloud Platform ופרויקט שבו מופעל חיוב, כדאי לעיין במדריך תחילת העבודה עם הפלטפורמה של מפות Google ליצירת חשבון לחיוב ופרויקט.

  1. ב-Cloud Console, לוחצים על התפריט הנפתח של הפרויקט ובוחרים את הפרויקט שבו רוצים להשתמש ב-Codelab הזה.

  1. מפעילים את ממשקי ה-API ואת ערכות ה-SDK של מפות Google הנדרשים למעבדת קוד זו ב-Google Cloud Marketplace. כדי לעשות זאת, יש לבצע את השלבים המפורטים בסרטון הזה או בתיעוד הזה.
  2. יוצרים מפתח API בדף פרטי הכניסה ב-Cloud Console. ניתן לבצע את השלבים המפורטים בסרטון הזה או בתיעוד הזה. לכל הבקשות שנשלחות לפלטפורמה של מפות Google נדרש מפתח API.

3. להורדת הקוד למתחילים

כדי לעזור לך להתחיל במהירות האפשרית, לפניך קוד התחלה שיעזור לך לעקוב אחר שיעור Lab זה. אתם מוזמנים לקפוץ לפתרון, אבל אם אתם רוצים להמשיך בכל השלבים בעצמכם, המשיכו לקרוא.

  1. משכפלים את המאגר אם git מותקן.
git clone https://github.com/googlecodelabs/maps-ios-swiftui.git

לחלופין, אפשר ללחוץ על הלחצן הבא כדי להוריד את קוד המקור.

  1. עם קבלת הקוד, במסוף cd אל המחוז starter/GoogleMapsSwiftUI.
  2. כדי להוריד את ה-SDK של מפות Google ל-iOS, יש להפעיל carthage update --platform iOS
  3. לבסוף, פותחים את הקובץ GoogleMapsSwiftUI.xcodeproj ב-Xcode

4. סקירה כללית של הקוד

בפרויקט למתחילים שהורדת, השיעורים הבאים סופקו לך ויושמו עבורך:

  • AppDelegate - האפליקציה של UIApplicationDelegate. כאן יופעל ה-SDK של מפות Google ל-iOS.
  • City - מבנה שמייצג עיר (מכיל שם וקואורדינטה של העיר).
  • MapViewController – ממשק משתמש פשוט ב-UIKit UIViewController שמכיל מפת Google (GMSmapView)
  • SceneDelegate - האפליקציה של UIWindowSceneDelegate שממנה נוצר אובייקט ContentView.

בנוסף, לכיתות הבאות יש הטמעות חלקיות והן יבוצעו על ידך עד סוף ה-Codelab הזה:

  • ContentView – תצוגת ה-SwitUI ברמה העליונה שמכילה את האפליקציה שלך.
  • MapViewControllerBridge – כיתה שמגשרת עם תצוגה של UIKit לתצוגה של SwiftUI. באופן ספציפי, זוהי הכיתה שתהפוך את MapViewController לנגיש ב-SwiifUI.

5. באמצעות SwiftUI לעומת UIKit

SwiftUI הושק ב-iOS 13 כמסגרת ממשק משתמש חלופית עבור UIKit, לפיתוח אפליקציות ל-iOS. בהשוואה ל-UIKit הקודם שלו, ל-SififUI יש כמה יתרונות. הנה כמה שמות:

  • התצוגות מתעדכנות באופן אוטומטי כאשר המצב משתנה. שימוש באובייקטים שנקרא State, כל שינוי בערך הבסיס שהוא מכיל יגרום לעדכון ממשק המשתמש באופן אוטומטי.
  • התצוגה המקדימה בזמן אמת מאפשרת פיתוח מהיר יותר. תצוגות מקדימות בשידור חי מצמצמות את הצורך בבנייה ובפריסה של קוד לאמולטור כדי לראות שינויים חזותיים בתצוגה מקדימה של התצוגה המפורטת של SwiftUI ב-Xcode.
  • מקור העובדות הוא בסוויפט. כל הצפיות ב-SififUI מוצהרות ב-Swift, כך שאין יותר צורך להשתמש ב-API Builder.
  • אינטראקציה עם UIKit. יכולת הפעולה ההדדית עם UIKit מבטיחה שאפליקציות קיימות יוכלו להשתמש ב-SwiftUI יחד עם התצוגות הקיימות שלהן. כמו כן, ניתן להשתמש בספריות ב-SwiifUI שעדיין לא תומכות בספריות SwiftUI, כמו ה-SDK של מפות Google ל-iOS.

יש גם כמה חסרונות:

  • SwiftUI זמין רק ב-iOS 13 ומעלה.
  • לא ניתן לבדוק את היררכיית התצוגה המקדימה בתצוגות מקדימות של Xcode.

מצב SwiftUI וזרימת הנתונים

SwiftUI מציע דרך חדשנית ליצירת ממשק משתמש בגישה הצהרתית - עליך לומר ל-SwittUI כיצד ברצונך שהתצוגה המפורטת שלך תבחן את כל המצבים השונים, והמערכת לעשות את כל השאר. SwiftUI מטפל בעדכון התצוגה בכל פעם שמצב הבסיס משתנה עקב אירוע או פעולת משתמש. העיצוב הזה נקרא בדרך כלל תהליך נתונים חד-כיווני. למרות שהעיצוב הספציפי הזה אינו בטווח של מעבדת הקוד הזו, מומלץ לקרוא איך זה עובד בתיעוד של Apple's בנושא מצב וזרימת נתונים.

מגשר בין UIKit ו-SwitUI באמצעות UIViewRepresentable או UIViewControllerRepresentable

מכיוון שה-SDK של מפות Google ל-iOS מבוסס על UIKit, ועדיין לא מספק תצוגה תואמת ל-SwiifUI, צריך להשתמש בו ב-SwiifUI כדי להתאים ל-UIViewRepresentable או ל-UIViewControllerRepresentable. הפרוטוקולים האלה מאפשרים ל-SwiiftUI לכלול UIView ו-UIViewController שנוצרו על ידי UIKit, בהתאמה. ניתן להשתמש בכל אחד מהפרוטוקולים כדי להוסיף מפת Google לתצוגה SwiftUI, אבל בשלב הבא נבחן את הפונקציה UIViewControllerRepresentable כדי לכלול קובץ UIViewController שמכיל מפה.

6. הוספת מפה

בקטע הזה, מוסיפים את מפות Google לתצוגה של SwiftUI.

add-a-map-צילומי@@xx

הוספת מפתח API

צריך לספק את מפתח ה-API שיצרתם בשלב קודם ל-SDK של מפות Google ל-iOS כדי לשייך את החשבון למפה שתוצג באפליקציה.

כדי לספק את מפתח ה-API, יש לפתוח את הקובץ AppDelegate.swift ולעבור אל השיטה application(_, didFinishLaunchingWithOptions). נכון לעכשיו, ה-SDK מופעל דרך GMSServices.provideAPIKey() עם המחרוזת "YOUR_API_KEY". צריך להחליף את המחרוזת הזו במפתח ה-API. אחרי ביצוע השלב הזה, להפעיל את SDK של מפות Google ל-iOS עם הפעלת האפליקציה.

הוספת מפה של Google באמצעות MapViewControllerBiger

לאחר שמפתח ה-API שלכם מועבר ל-SDK, השלב הבא הוא להציג את המפה באפליקציה.

בקר התצוגה המפורטת שמספק את הקוד למתחילים, MapViewController מכיל כרגע GMSMapView בתצוגה המפורטת שלו. עם זאת, מאחר שבקר התצוגה הזה נוצר ב-UIKit, יהיה עליך לגשר את הכיתה הזו אל SwiftUI כדי שניתן יהיה להשתמש בה בתוך ContentView. כך נכנסים לדף:

  1. פותחים את הקובץ MapViewControllerBridge ב-Xcode.

סיווג זה תואם ל-UIViewControllerRepresentable, שהוא הפרוטוקול הנדרש לאחזור ממשק משתמש של UIKit UIViewController כדי שניתן יהיה להשתמש בו כתצוגת SwiftUI. כלומר, תאימות לפרוטוקול זה מאפשרת לגשר על תצוגה של UIKit לתצוגה של SwiftUI. כדי להשתמש בפרוטוקול הזה, צריך להטמיע שתי שיטות:

  • makeUIViewController(context) – השיטה הזו נקראת SwiftUI כדי ליצור את UIViewController הבסיסי. כאן אפשר ליצור את הUIViewController ולעבור אותו במצב הראשוני.
  • updateUIViewController(_, context) – השיטה הזו נקראת SwiftUI בכל פעם שהמצב משתנה. כאן מבצעים את השינויים הרצויים ב-UIViewController כדי להגיב לשינוי במצב.
  1. צור MapViewController

בתוך הפונקציה makeUIViewController(context), יוצרים אובייקט MapViewController חדש ומחזירים אותו כתוצאה. לאחר מכן, ה-MapViewControllerBridge אמור להיראות כך:

MapViewControllerBרידג'

import GoogleMaps
import SwiftUI

struct MapViewControllerBridge: UIViewControllerRepresentable {

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

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

שימוש ב-MapViewControllerBiger ב-ContentView

כעת, אם MapViewControllerBridge יוצר מופע של MapViewController, השלב הבא הוא להשתמש במבנה זה ב-ContentView כדי להציג מפה.

  1. פותחים את הקובץ ContentView ב-Xcode.

נוצר אובייקט ContentView ב-SceneDelegate ומכיל את תצוגת האפליקציה ברמה העליונה. המפה תתווסף מתוך הקובץ הזה.

  1. יצירת MapViewControllerBridge בתוך הנכס body.

בנכס body של הקובץ הזה כבר סופק ערך ZStack והוא הוטמע עבורך. ה-ZStack מכיל כרגע רשימה של ערים שניתנות לאינטראקציה ולגרירה, שישמשו בשלב מאוחר יותר. בשלב זה, בZStack יש ליצור MapViewControllerBridge בתור תצוגת הצאצא הראשונה של ZStack כך שמפה תוצג באפליקציה שמאחורי רשימת הערים. לאחר מכן, התוכן של הנכס body בתוך 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. עכשיו הגיע הזמן להריץ את האפליקציה. כעת אתה אמור לראות את טעינת המפה במסך של המכשיר יחד עם רשימה של ערים שניתן לגרור אותן אל תחתית המסך.

7. הוספת סמנים למפה

בשלב הקודם, הוספתם מפה לצד רשימה אינטראקטיבית שבה מוצגת רשימה של ערים. בקטע הזה, תוסיפו סמנים לכל עיר ברשימה.

map-with-marks@2x.png

סמנים כמצב

ContentView מצהיר כרגע על נכס בשם markers, שהוא רשימה של GMSMarker המייצגת כל עיר המוצהרת בנכס cities הסטטי. שימו לב לנכס הזה יש הערות עם מאפיין WiftUI של הנכס WraptUI State כדי לציין שהוא ינוהל על ידי SwiftUI. לכן, אם יזוהו שינויים בנכס הזה, כגון הוספה או הסרה של סמן, צפיות שמשתמשים במצב הזה יעודכנו.

צפייה בתוכן

  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
  }

לידיעתך, ContentView משתמש בנכס markers כדי לעבד את רשימת הערים על ידי העברתו למחלקה 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)
      }
    }
  }
}

מעביר את המדינה אל MapViewControllerBbridge באמצעות כריכה

בנוסף לרשימת הערים שבהן מוצגים נתונים מהנכס markers, יש להעביר את הנכס הזה למבנה MapViewControllerBridge כדי שניתן יהיה להציג את הסמנים האלה במפה. לשם כך:

  1. יש להצהיר על נכס markers חדש ב-MapViewControllerBridge עם הערות לגבי @Binding

MapViewControllerBרידג'

struct MapViewControllerBridge: : UIViewControllerRepresentable {
  @Binding var markers: [GMSMarker]
  // ...
}
  1. ב-MapViewControllerBridge, יש לעדכן את השיטה updateUIViewController(_, context) כדי להשתמש בנכס markers

כפי שצוין בשלב הקודם, updateUIViewController(_, context) יתקשר אל SwiftUI בכל פעם שהמצב ישתנה. בשיטה הזו אנחנו רוצים לעדכן את המפה, ולכן עליך להציג את הסמנים בmarkers. לשם כך, עליך לעדכן את המאפיין map בכל סמן. לאחר ביצוע השלב הזה, ה-MapViewControllerBridge אמור להיראות כך:

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. יש להעביר את הנכס markers מ-ContentView אל MapViewControllerBridge

מאחר שהוספת נכס חדש ב-MapViewControllerBridge, עכשיו צריך להעביר את הערך של הנכס הזה באתחול של MapViewControllerBridge. לכן, אם תנסו ליצור את האפליקציה, שימו לב שהיא לא תספור. כדי לפתור את הבעיה, צריך לעדכן את כתובת ה-ContentView שבה נוצר ה-MapViewControllerBridge ולהעביר אותו בנכס של markers כך:

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

חשוב לשים לב שהקידומת $ שימשה להעברת נתונים ב-markers אל MapViewControllerBridge, כי היא צפויה לכלול נכס משויך. $ היא קידומת שמורה לשימוש בwrapper של נכס Swift. אחרי שמחילים אותו על מדינה, היא תחזיר איגוד.

  1. יש להריץ את האפליקציה כדי לראות את הסמנים המוצגים במפה.

8. אנימציה לעיר שנבחרה

בשלב הקודם, הוספת סמנים למפה על ידי העברת מצב מתצוגת SwiftUI אחת לאחרת. בשלב זה, המערכת תשיק אנימציה של עיר/סמן לאחר ההקשה ברשימת הפריטים האינטראקטיביים. כדי לבצע את האנימציה, עליך להגיב לשינויים במצב על ידי שינוי מיקום המצלמה של המפה כשהשינוי מתבצע. למידע נוסף על הקונספט של המצלמה במפה, ניתן לעיין במאמר מצלמה ותצוגה.

אנימטי-עיר@2x.png

יצירת אנימציה של המפה בעיר שנבחרה

כדי להנפיש את העיר בעיר שנבחרה:

  1. הגדרת חיוב חדש ב-MapViewControllerBridge

ב-ContentView יש מאפיין מדינה בשם selectedMarker, שאתחול שלו מצוין במספר n ועודכן בכל פעם שהעיר נבחרת ברשימה. פעולה זו מטופלת על ידי התצוגה המפורטת CitiesList buttonAction בתוך ContentView.

צפייה בתוכן

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

בכל פעם ש-selectedMarker משתנה, MapViewControllerBridge צריך להיות מודע לשינוי במצב הזה כדי שניתן יהיה להנפיש את המפה לסמן שנבחר. לכן, צריך להגדיר קישור חדש ב-MapViewControllerBridge מסוג GMSMarker ולתת שם לנכס selectedMarker.

MapViewControllerBרידג'

struct MapViewControllerBridge: UIViewControllerRepresentable {
  @Binding var selectedMarker: GMSMarker?
}
  1. יש לעדכן את MapViewControllerBridge כדי ליצור אנימציה במפה בכל פעם שselectedMarker משתנה

אחרי הכרזה על קישור חדש, צריך לעדכן את הפונקציה updateUIViewController_, context) של MapViewControllerBridge כך שהמפה תסומן לסמן שנבחר. כדי לעשות זאת צריך להעתיק את הקוד שבהמשך:

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

הפונקציה animateToSelectedMarker(viewController) תבצע רצף של אנימציות במפה באמצעות הפונקציה animate(with) של GMSMapView&#39.

  1. להעביר את selectedMarker של ContentView אל MapViewControllerBridge

אחרי ההצהרה על הקישור החדש של MapViewControllerBridge, יש לעדכן את ContentView. לאחר מכן, יש להעביר את הקובץ אל selectedMarker במקום שבו MapViewControllerBridge נוצר.

צפייה בתוכן

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

השלמת שלב זה תגרום להנפשה במפה בכל פעם שעיר חדשה נבחרת ברשימה.

תצוגת אנימציה של SwiftUI כדי להדגיש את העיר

SwiftUI מאפשר ליצור אנימציות בקלות רבה, משום שהוא יטפל באנימציות של מעברים בין מדינות. כדי להמחיש זאת, מוסיפים אנימציות נוספות על ידי התמקדות בתצוגה לעיר שנבחרה לאחר השלמת האנימציה במפה. כדי לעשות זאת, יש לבצע את השלבים הבאים:

  1. הוספת סגירה של onAnimationEnded אל MapViewControllerBridge

האנימציה של SwiftUI תבוצע לאחר רצף האנימציות במפה שהוספת בעבר, ולכן עליך להצהיר על סגירה חדשה בשם onAnimationEnded תוך MapViewControllerBridge. היא תופעל לאחר הסגירה של 0.5 שניות אחרי האנימציה האחרונה במפה בתוך השיטה animateToSelectedMarker(viewController).

MapViewControllerBרידג'

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. הטמעת onAnimationEnded ב-MapViewControllerBridge

מטמיעים את הסגירה onAnimationEnded שבה MapViewControllerBridge מופיעה בתוך ContentView. יש להעתיק ולהדביק את הקוד הבא, שמוסיף מצב חדש בשם zoomInCenter. בנוסף, הוא משנה את התצוגה באמצעות clipShape ומשתנה בקוטר של הצורה שנחתכה, בהתאם לערך של zoomInCenter

צפייה בתוכן

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. קדימה, אפשר להריץ את האפליקציה כדי לראות את האנימציות!

9. שליחת אירוע אל SwiftUI

בשלב זה, יש להאזין לאירועים שנוצרו על ידי GMSMapView ולשלוח את האירוע ל-SififUI. באופן ספציפי, יש להגדיר גישה אל תצוגת המפה ולהאזין לאירועי תנועה במצלמה. כך, כשהעיר תתמקד והמצלמה במפה זזה מתנועה, תצוגת המפה תבטל את המיקוד שלה, כך שאפשר יהיה לראות יותר מקומות במפה.

שימוש במתאמים של SwiftUI

GMSMapView פולט אירועים כמו שינויים במיקום המצלמה או הקשה על סמן. המנגנון להאזנה לאירועים האלה הוא דרך הפרוטוקול GMSmapViewDelegate. SwiftUI מציג את הקונספט של מתאם שמשמש במיוחד כדי להעניק גישה לנאמני מידע של ViewKit. לכן, בעולם ה-SififUI, על המתאם להיות אחראי לתאימות לפרוטוקול GMSMapViewDelegate. כדי לעשות זאת, יש לבצע את השלבים הבאים:

  1. יצירת מתאם בשם MapViewCoordinator בתוך MapViewControllerBridge

יש ליצור כיתה מקוננת בתוך הכיתה MapViewControllerBridge ולקרוא לה MapViewCoordinator. הכיתה צריכה לעמוד ב-GMSMapViewDelegate ועליה להצהיר על MapViewControllerBridge כנכס.

MapViewControllerBרידג'

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

    init(_ mapViewControllerBridge: MapViewControllerBridge) {
      self.mapViewControllerBridge = mapViewControllerBridge
    }
  }
}
  1. הטמעת makeCoordinator() ב-MapViewControllerBridge

לאחר מכן יש ליישם את השיטה makeCoordinator() בתוך MapViewControllerBridge ולהחזיר מופע של MapViewCoodinator שיצרת בשלב הקודם.

MapViewControllerBרידג'

struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  func makeCoordinator() -> MapViewCoordinator {
    return MapViewCoordinator(self)
  }
}
  1. הגדרת MapViewCoordinator בתור תצוגת המפה

לאחר יצירת המתאם המותאם אישית, השלב הבא הוא להגדיר את המתאם כנציג מורשה של תצוגת המפה של. לשם כך יש לעדכן את אתחול בקר התצוגה ב-makeUIViewController(context). ניתן יהיה לגשת למתאם שנוצר מהשלב הקודם מאובייקט ההקשר.

MapViewControllerBרידג'

struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  func makeUIViewController(context: Context) -> MapViewController {
    let uiViewController = MapViewController()
    uiViewController.map.delegate = context.coordinator
    return uiViewController
  }
  1. צריך להוסיף סגירה ל-MapViewControllerBridge כדי שהמצלמה תפיץ אירוע למעלה

המטרה היא לעדכן את התצוגה עם תנועות המצלמה, ולכן יש להצהיר על מאפיין סגירה חדש שמקבל את הערך הבוליאני ב-MapViewControllerBridge שנקרא mapViewWillMove ולהפעיל את הסגירה הזו בשיטה 'הענקת גישה' mapView(_, willMove) בתוך MapViewCoordinator. יש להעביר את הערך של gesture לסגירה כדי שהתצוגה של SwiftUI תוכל להגיב רק לאירועי תנועה במצלמה שקשורים לתנועות.

MapViewControllerBרידג'

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

  final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
    // ...
    func mapView(_ mapView: GMSMapView, willMove gesture: Bool) {
      self.mapViewControllerBridge.mapViewWillMove(gesture)
    }
  }
}
  1. יש לעדכן את ContentView כדי להעביר ערך עבור mapWillMove

עם הסגירה החדשה ב-MapViewControllerBridge, יש לעדכן את ContentView כך שיעביר ערך עבור הסגירה החדשה. בתוך סגירה, מעבירים את מצב zoomInCenter למצב false אם האירוע בהעברה קשור לתנועה. פעולה זו תציג שוב את המפה בתצוגה מלאה כאשר המפה זזה.

צפייה בתוכן

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. כדאי לך להריץ את האפליקציה כדי לראות את השינויים החדשים!

10. מזל טוב

כל הכבוד! הגעת ליעד! הצלחתם ללמוד הרבה, ואנחנו מקווים שבזכות השיעורים האלה למדתם איך לבנות את אפליקציית SwiftUI שלכם באמצעות ה-SDK של מפות Google ל-iOS.

מה למדת

  • ההבדלים בין SwiftUI ל-UIKit
  • איך לגשר בין SwiftUI ל-UIKit באמצעות UIViewControllerRepresentable
  • איך לשנות את תצוגת המפה בעזרת מדינה וקישור
  • איך שולחים אירוע מתצוגת המפה אל SwiftUI באמצעות מתאם

מה עכשיו?

  • SDK של מפות Google ל-iOS - תיעוד רשמי של ה-SDK של מפות Google ל-iOS
  • Place SDK ל-iOS – חיפוש עסקים מקומיים ונקודות עניין בסביבה
  • maps-sdk-for-ios-samples – קוד לדוגמה ב-GitHub שמדגים את כל התכונות שב-SDK של מפות Google ל-iOS.
  • SwiftUI – התיעוד הרשמי של Apple&#39 ב-SwiftUI
  • ענו על השאלה הבאה כדי לעזור לנו ליצור את התוכן השימושי ביותר:

אילו מעבדי קוד אחרים היית רוצה לראות?

תצוגה חזותית של נתונים במפות מידע נוסף על התאמה אישית של סגנון המפות שלי יצירת אינטראקציות בתלת-ממד במפות

האם קוד הקוד שאתם לא רוצים מופיע למעלה? אפשר לבקש אותו באמצעות בעיה חדשה כאן.