1. לפני שתתחיל
בשיעור הזה תלמדו איך להשתמש ב-Maps SDK ל-iOS עם SwiftUI.
דרישות מוקדמות
- ידע בסיסי ב-Swift
- היכרות בסיסית עם SwiftUI
הפעולות שתבצעו:
- אפשר להפעיל את Maps SDK for iOS ולהשתמש בו כדי להוסיף את מפות Google לאפליקציית iOS באמצעות SwiftUI.
- מוסיפים סמנים למפה.
- העברת מצב בין SwiftUI לבין אובייקט
GMSMapView
.
מה נדרש
- Xcode 11.0 ואילך
- חשבון Google שמוגדר בו חיוב
- Maps SDK ל-iOS
- קרתגו
2. להגדרה
בשלב הבא של ההפעלה, מפעילים את Maps SDK ל-iOS.
הגדרת הפלטפורמה של מפות Google
אם עדיין אין לכם חשבון ב-Google Cloud Platform ופרויקט עם חיוב מופעל, תוכלו לעיין במדריך תחילת העבודה עם הפלטפורמה של מפות Google כדי ליצור חשבון לחיוב ופרויקט.
- בCloud Console, לוחצים על התפריט הנפתח של הפרויקט ובוחרים את הפרויקט שבו רוצים להשתמש ב-codelab הזה.
- מפעילים ב-Google Cloud Marketplace את ממשקי ה-API וערכות ה-SDK של הפלטפורמה של מפות Google שנדרשים ל-codelab הזה. כדי לעשות זאת, פועלים לפי השלבים בסרטון הזה או בתיעוד הזה.
- יוצרים מפתח API בדף Credentials במסוף Cloud. אפשר לפעול לפי השלבים שמפורטים בסרטון הזה או בתיעוד הזה. כל הבקשות אל הפלטפורמה של מפות Google מחייבות מפתח API.
3. הורדת קוד לתחילת הדרך
כדי לעזור לכם להתחיל כמה שיותר מהר, הנה קוד התחלתי שיעזור לכם לעקוב אחרי ההוראות במעבדת התכנות הזו. אתם יכולים לדלג לפתרון, אבל אם אתם רוצים לבצע את כל השלבים בעצמכם, כדאי להמשיך לקרוא.
- משכפלים את המאגר אם
git
מותקן.
git clone https://github.com/googlecodelabs/maps-ios-swiftui.git
לחלופין, אפשר ללחוץ על הלחצן הבא כדי להוריד את קוד המקור.
- אחרי שמקבלים את הקוד, במסוף
cd
נכנסים לספרייהstarter/GoogleMapsSwiftUI
. - מריצים את הפקודה
carthage update --platform iOS
כדי להוריד את Maps SDK ל-iOS - לבסוף, פותחים את קובץ
GoogleMapsSwiftUI.xcodeproj
ב-Xcode.
4. סקירה כללית של הקוד
בפרויקט המתחיל שהורדתם, הכיתות הבאות סופקו והוטמעו עבורכם:
-
AppDelegate
–UIApplicationDelegate
של האפליקציה. כאן יתבצע האתחול של Maps SDK ל-iOS. -
City
– מבנה שמייצג עיר (מכיל שם וקואורדינטות של העיר). -
MapViewController
– UIKit מצומצםUIViewController
שמכיל מפת Google (GMSMapView)-
SceneDelegate
–UIWindowSceneDelegate
של האפליקציה שממנה נוצרתContentView
.
-
בנוסף, המחלקות הבאות כוללות הטמעות חלקיות, ואתם תצטרכו להשלים אותן עד סוף ה-Codelab הזה:
-
ContentView
– התצוגה ברמה העליונה של SwiftUI שמכילה את האפליקציה. -
MapViewControllerBridge
– מחלקה שמגשרת בין תצוגת UIKit לתצוגת SwiftUI. באופן ספציפי, זהו המחלקה שתאפשר גישה ל-MapViewController
ב-SwiftUI.
5. SwiftUI לעומת UIKit
SwiftUI הוצגה ב-iOS 13 כחלופה ל-UIKit, שהיא מסגרת ממשק משתמש לפיתוח אפליקציות ל-iOS. ל-SwiftUI יש כמה יתרונות בהשוואה ל-UIKit, הגרסה הקודמת שלו. לדוגמה:
- התצוגות מתעדכנות אוטומטית כשהמצב משתנה. באמצעות אובייקטים שנקראים State, כל שינוי בערך הבסיסי שהם מכילים יגרום לעדכון אוטומטי של ממשק המשתמש.
- תצוגות מקדימות בזמן אמת מאפשרות פיתוח מהיר יותר. תצוגות מקדימות בזמן אמת מצמצמות את הצורך ליצור ולפרוס קוד לאמולטור כדי לראות שינויים חזותיים, כי אפשר לראות בקלות תצוגה מקדימה של תצוגת SwiftUI ב-Xcode.
- מקור האמת נמצא ב-Swift. כל התצוגות ב-SwiftUI מוצהרות ב-Swift, כך שאין יותר צורך להשתמש ב-Interface Builder.
- פועל בשילוב עם UIKit. התאימות ל-UIKit מבטיחה שאפליקציות קיימות יוכלו להשתמש ב-SwiftUI באופן הדרגתי עם התצוגות הקיימות שלהן. בנוסף, אפשר להשתמש ב-SwiftUI גם בספריות שעדיין לא תומכות ב-SwiftUI, כמו Maps SDK ל-iOS.
יש גם כמה חסרונות:
- SwiftUI זמין רק ב-iOS 13 ואילך.
- אי אפשר לבדוק את היררכיית התצוגה בתצוגות מקדימות של Xcode.
מצב וזרימת נתונים ב-SwiftUI
SwiftUI מציעה דרך חדשה ליצור ממשק משתמש באמצעות גישה הצהרתית – אתם אומרים ל-SwiftUI איך אתם רוצים שהתצוגה תיראה, כולל כל המצבים השונים שלה, והמערכת תעשה את השאר. SwiftUI מטפל בעדכון התצוגה בכל פעם שמתרחש שינוי במצב הבסיסי בגלל אירוע או פעולת משתמש. העיצוב הזה נקרא בדרך כלל זרימת נתונים חד-כיוונית. הפרטים הספציפיים של העיצוב הזה לא נכללים ב-codelab הזה, אבל מומלץ לקרוא את התיעוד של Apple בנושא State and Data Flow כדי להבין איך זה עובד.
גישור בין UIKit ו-SwiftUI באמצעות UIViewRepresentable או UIViewControllerRepresentable
Maps SDK for iOS מבוסס על UIKit, ולא מספק תצוגה שתואמת ל-SwiftUI. לכן, כדי להשתמש בו ב-SwiftUI, צריך להתאים אותו ל-UIViewRepresentable
או ל-UIViewControllerRepresentable
. הפרוטוקולים האלה מאפשרים ל-SwiftUI לכלול רכיבי UIView
ו-UIViewController
שנבנו באמצעות UIKit. אפשר להשתמש בכל אחד מהפרוטוקולים כדי להוסיף מפת Google לתצוגת SwiftUI, אבל בשלב הבא נראה איך להשתמש ב-UIViewControllerRepresentable
כדי לכלול UIViewController
שמכיל מפה.
6. הוספת מפה
בקטע הזה תוסיפו את מפות Google לתצוגת SwiftUI.
הוספת מפתח API
צריך לספק את מפתח ה-API שיצרתם בשלב קודם ל-Maps SDK for iOS כדי לשייך את החשבון שלכם למפה שתוצג באפליקציה.
כדי לספק את מפתח ה-API, פותחים את הקובץ ועוברים אל ה-method application(_, didFinishLaunchingWithOptions)
.AppDelegate.swift
ה-SDK מאותחל באמצעות GMSServices.provideAPIKey()
עם המחרוזת YOUR_API_KEY. מחליפים את המחרוזת במפתח ה-API. השלמת השלב הזה תפעיל את Maps SDK for iOS כשהאפליקציה תופעל.
הוספה של מפת Google באמצעות MapViewControllerBridge
עכשיו, כשמפתח ה-API מסופק ל-SDK, השלב הבא הוא להציג את המפה באפליקציה.
בבקר התצוגה שמסופק בקוד ההתחלתי, MapViewController
יש GMSMapView
בתצוגה שלו. עם זאת, מכיוון שבקר התצוגה הזה נוצר ב-UIKit, תצטרכו לגשר בין המחלקה הזו ל-SwiftUI כדי שתוכלו להשתמש בה בתוך ContentView
. לשם כך:
- פותחים את הקובץ
MapViewControllerBridge
ב-Xcode.
המחלקות האלה תואמות ל-UIViewControllerRepresentable, שהוא הפרוטוקול שנדרש כדי לעטוף את UIKit UIViewController
כדי שאפשר יהיה להשתמש בו כתצוגת SwiftUI. במילים אחרות, התאמה לפרוטוקול הזה מאפשרת לגשר בין תצוגת UIKit לתצוגת SwiftUI. כדי לפעול בהתאם לפרוטוקול הזה, צריך להטמיע שתי שיטות:
-
makeUIViewController(context)
– השיטה הזו מופעלת על ידי SwiftUI כדי ליצור אתUIViewController
הבסיסי. כאן יוצרים מופע שלUIViewController
ומעבירים לו את המצב ההתחלתי. -
updateUIViewController(_, context)
– השיטה הזו מופעלת על ידי SwiftUI בכל פעם שמתבצע שינוי במצב. כאן מבצעים שינויים ב-UIViewController
כדי להגיב לשינוי במצב.
- צור
MapViewController
בתוך הפונקציה makeUIViewController(context)
, יוצרים מופע חדש של MapViewController
ומחזירים אותו כתוצאה. אחרי שעושים את זה, MapViewControllerBridge
אמור להיראות כך:
MapViewControllerBridge
import GoogleMaps
import SwiftUI
struct MapViewControllerBridge: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> MapViewController {
return MapViewController()
}
func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
}
}
שימוש ב-MapViewControllerBridge ב-ContentView
עכשיו, אחרי ש-MapViewControllerBridge
יוצר מופע של MapViewController
, השלב הבא הוא להשתמש במבנה הזה בתוך ContentView
כדי להציג מפה.
- פותחים את הקובץ
ContentView
ב-Xcode.
האובייקט ContentView
נוצר ב-SceneDelegate
ומכיל את תצוגת האפליקציה ברמה העליונה. המפה תתווסף מתוך הקובץ הזה.
- יוצרים
MapViewControllerBridge
בנכסbody
.
במאפיין body
של הקובץ הזה, כבר סופק והוטמע בשבילכם ZStack
. ה-ZStack
מכיל רשימה של ערים שאפשר לגרור ולקיים איתה אינטראקציה, ותשתמשו בה בשלב מאוחר יותר. בשלב הזה, בתוך ZStack
יוצרים MapViewControllerBridge
כצפייה הראשונה בצאצא של ZStack
, כדי שמפה תוצג באפליקציה מאחורי רשימת הערים. אחרי שתעשו את זה, התוכן של הנכס body
בתוך ContentView
אמור להיראות כך:
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()
} // ...
}
}
}
- עכשיו מריצים את האפליקציה. המפה אמורה להיטען במסך המכשיר, וגם רשימה של ערים שאפשר לגרור אותה לחלק התחתון של המסך.
7. הוספת סמנים למפה
בשלב הקודם הוספתם מפה לצד רשימה אינטראקטיבית של ערים. בקטע הזה מוסיפים סמנים לכל עיר ברשימה.
סמנים כסטטוס
ContentView
מכריז על מאפיין בשם markers
שהוא רשימה של GMSMarker
שמייצגת כל עיר שהוכרזה במאפיין הסטטי cities
. שימו לב שהמאפיין הזה מסומן בהערה עם עטיפת המאפיין State של SwiftUI, כדי לציין שהוא צריך להיות מנוהל על ידי SwiftUI. לכן, אם המערכת תזהה שינויים בנכס הזה, כמו הוספה או הסרה של סמן, התצוגות שמשתמשות במצב הזה יעודכנו.
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
}
שימו לב שרכיב ContentView
משתמש במאפיין markers
כדי להציג את רשימת הערים על ידי העברה שלו למחלקה 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)
}
}
}
}
העברת מצב ל-MapViewControllerBridge באמצעות @Binding
בנוסף לרשימת הערים שמוצגים בה נתונים מהנכס markers
, צריך להעביר את הנכס הזה למבנה MapViewControllerBridge
כדי שאפשר יהיה להשתמש בו להצגת הסמנים האלה במפה. כדי לעשות זאת:
- מצהירים על מאפיין
markers
חדש בתוךMapViewControllerBridge
עם ההערה@Binding
MapViewControllerBridge
struct MapViewControllerBridge: : UIViewControllerRepresentable {
@Binding var markers: [GMSMarker]
// ...
}
- ב-
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 }
}
}
- העברת הנכס
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
, כי הוא מצפה למאפיין מאוגד. $
היא תחילית שמורה לשימוש עם עטיפות מאפיינים של Swift. כשמחילים אותה על State, היא מחזירה Binding.
- מריצים את האפליקציה כדי לראות את הסמנים שמוצגים במפה.
8. הוספת אנימציה למעבר לעיר שנבחרה
בשלב הקודם הוספתם סמנים למפה על ידי העברת מצב מתצוגת SwiftUI אחת לתצוגה אחרת. בשלב הזה, תפעילו אנימציה של מעבר לעיר או לסמן אחרי שהמשתמש יקיש על פריט ברשימה האינטראקטיבית. כדי להפעיל את האנימציה, מגיבים לשינויים בסטטוס על ידי שינוי המיקום של המצלמה במפה כשהשינוי מתרחש. מידע נוסף על הרעיון של המצלמה במפה זמין במאמר מצלמה ותצוגה.
הוספת אנימציה למפה כדי להציג את העיר שנבחרה
כדי להוסיף אנימציה למפה של עיר שנבחרה:
- הגדרת קישור חדש ב-
MapViewControllerBridge
ל-ContentView
יש מאפיין State שנקרא selectedMarker
, שמוגדר כ-nil ומתעדכן בכל פעם שנבחרת עיר מהרשימה. הנתונים האלה מוצגים בתצוגה CitiesList
buttonAction
ב-ContentView
.
ContentView
CitiesList(markers: $markers) { (marker) in
guard self.selectedMarker != marker else { return }
self.selectedMarker = marker
// ...
}
בכל פעם ש-selectedMarker
משתנה, MapViewControllerBridge
צריך להיות מודע לשינוי הזה במצב כדי שיוכל להנפיש את המפה לסמן שנבחר. לכן, מגדירים Binding חדש בתוך MapViewControllerBridge
מסוג GMSMarker
ונותנים לנכס את השם selectedMarker
.
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
@Binding var selectedMarker: GMSMarker?
}
- מעדכנים את
MapViewControllerBridge
כדי להנפיש את המפה בכל פעם ש-selectedMarker
משתנה
אחרי שמצהירים על Binding חדש, צריך לעדכן את הפונקציה MapViewControllerBridge
updateUIViewController_, context)
כך שהמפה תציג אנימציה של המעבר לסמן שנבחר. כדי לעשות זאת, מעתיקים את הקוד הבא:
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
.
- העברת
selectedMarker
שלContentView
אלMapViewControllerBridge
אחרי ש-MapViewControllerBridge
יכריז על ה-Binding החדש, צריך לעדכן את ContentView
כדי להעביר את selectedMarker
במקום שבו נוצר מופע של MapViewControllerBridge
.
ContentView
struct ContentView: View {
// ...
var body: some View {
// ...
GeometryReader { geometry in
ZStack(alignment: .top) {
// Map
MapViewControllerBridge(markers: $markers, selectedMarker: $selectedMarker)
// ...
}
}
}
}
השלמת השלב הזה תגרום לכך שהמפה תציג אנימציה בכל פעם שתיבחר עיר חדשה ברשימה.
הנפשת תצוגת SwiftUI כדי להדגיש עיר
SwiftUI מפשט את תהליך האנימציה של תצוגות, כי הוא מטפל בביצוע אנימציות למעברים בין מצבים. כדי להמחיש את זה, תוסיפו עוד אנימציות על ידי מיקוד התצוגה בעיר שנבחרה אחרי שאנימציית המפה תסתיים. כדי לעשות זאת, פועלים לפי השלבים הבאים:
- הוספת חסימה של
onAnimationEnded
אלMapViewControllerBridge
מכיוון שאנימציית ה-SwiftUI תתבצע אחרי רצף האנימציה של המפה שהוספתם קודם, צריך להצהיר על סגירה חדשה בשם onAnimationEnded
בתוך MapViewControllerBridge
ולהפעיל את הסגירה הזו אחרי השהיה של 0.5 שניות אחרי האנימציה האחרונה של המפה בתוך השיטה 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()
})
})
}
}
}
}
}
- הטמעה של
onAnimationEnded
ב-MapViewControllerBridge
מטמיעים את הסגירה onAnimationEnded
במקום שבו נוצר המופע MapViewControllerBridge
בתוך ContentView
. מעתיקים ומדביקים את הקוד הבא שמוסיף מצב חדש בשם zoomInCenter
וגם משנה את התצוגה באמצעות clipShape
ומשנה את הקוטר של הצורה החתוכה בהתאם לערך של 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))
}
}
}
}
- אפשר להפעיל את האפליקציה כדי לראות את האנימציות.
9. שליחת אירוע ל-SwiftUI
בשלב הזה, תפעילו listener לאירועים שמופקים מ-GMSMapView
ותשלחו את האירוע הזה ל-SwiftUI. בפרט, תגדירו נציג לתצוגת המפה ותאזינו לאירועים של תנועת המצלמה, כך שכאשר מתמקדים בעיר ומצלמת המפה זזה בעקבות תנועת אצבע, תצוגת המפה תבטל את המיקוד כדי שתוכלו לראות חלק גדול יותר של המפה.
שימוש ב-SwiftUI Coordinators
GMSMapView
שולח אירועים כמו שינויים במיקום המצלמה או הקשה על סמן. המנגנון להאזנה לאירועים האלה הוא באמצעות פרוטוקול GMSMapViewDelegate. ב-SwiftUI מוצג הרעיון של Coordinator, שמשמש באופן ספציפי כנציג של בקרי תצוגה ב-UIKit. לכן, בעולם של SwiftUI, רכיב Coordinator צריך להיות אחראי להתאמה לפרוטוקול GMSMapViewDelegate
. כדי לעשות זאת, מבצעים את השלבים הבאים:
- יצירת רכז בשם
MapViewCoordinator
בתוךMapViewControllerBridge
יוצרים מחלקה מקוננת בתוך המחלקה MapViewControllerBridge
וקוראים לה MapViewCoordinator
. המחלקות האלה צריכות להיות תואמות ל-GMSMapViewDelegate
ולהצהיר על MapViewControllerBridge
כמאפיין.
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
// ...
final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
var mapViewControllerBridge: MapViewControllerBridge
init(_ mapViewControllerBridge: MapViewControllerBridge) {
self.mapViewControllerBridge = mapViewControllerBridge
}
}
}
- הטמעה של
makeCoordinator()
ב-MapViewControllerBridge
לאחר מכן, מטמיעים את השיטה makeCoordinator()
בתוך MapViewControllerBridge
ומחזירים מופע של MapViewCoodinator
שיצרתם בשלב הקודם.
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
// ...
func makeCoordinator() -> MapViewCoordinator {
return MapViewCoordinator(self)
}
}
- הגדרת
MapViewCoordinator
כנציג של תצוגת המפה
אחרי שיוצרים את רכיב ה-Coordinator בהתאמה אישית, השלב הבא הוא להגדיר את רכיב ה-Coordinator כנציג של תצוגת המפה של בקר התצוגה. כדי לעשות זאת, צריך לעדכן את האתחול של בקר התצוגה ב-makeUIViewController(context)
. המתאם שנוצר בשלב הקודם יהיה נגיש מאובייקט ההקשר.
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
// ...
func makeUIViewController(context: Context) -> MapViewController {
let uiViewController = MapViewController()
uiViewController.map.delegate = context.coordinator
return uiViewController
}
}
- מוסיפים חסימה ל-
MapViewControllerBridge
כדי שאירוע התנועה של המצלמה יועבר
מכיוון שהמטרה היא לעדכן את התצוגה באמצעות תנועות המצלמה, צריך להגדיר מאפיין סגירה חדש שמקבל ערך בוליאני בתוך MapViewControllerBridge
שנקרא mapViewWillMove
, ולהפעיל את הסגירה הזו בשיטת ההקצאה mapView(_, willMove)
בתוך MapViewCoordinator
. מעבירים את הערך של gesture
לסגירה כדי שתצוגת ה-SwiftUI תוכל להגיב רק לאירועים של תנועות מצלמה שקשורות למחוות.
MapViewControllerBridge
struct MapViewControllerBridge: UIViewControllerRepresentable {
var mapViewWillMove: (Bool) -> ()
//...
final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
// ...
func mapView(_ mapView: GMSMapView, willMove gesture: Bool) {
self.mapViewControllerBridge.mapViewWillMove(gesture)
}
}
}
- צריך לעדכן את ContentView כדי להעביר ערך ל-
mapWillMove
בעקבות הסגירה החדשה שהוכרזה ב-MapViewControllerBridge
, צריך לעדכן את ContentView
כדי להעביר ערך לסגירה החדשה הזו. בתוך החסימה הזו, מעבירים את המתג של המצב zoomInCenter
למצב false
אם אירוע ההזזה קשור לתנועת אצבע. הפעולה הזו תציג את המפה שוב בתצוגה מלאה כשהמפה תועבר באמצעות תנועת מגע.
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
})
// ...
}
}
}
}
- אפשר להפעיל את האפליקציה כדי לראות את השינויים החדשים.
10. מזל טוב
כל הכבוד שהגעת עד לכאן! עברת הרבה שלבים, ואני מקווה שהשיעורים שלמדת מאפשרים לך עכשיו לבנות אפליקציית SwiftUI משלך באמצעות Maps SDK ל-iOS.
מה למדתם
- ההבדלים בין SwiftUI לבין UIKit
- איך מגשרים בין SwiftUI ל-UIKit באמצעות UIViewControllerRepresentable
- איך מבצעים שינויים בתצוגת המפה באמצעות State ו-Binding
- איך שולחים אירוע מתצוגת המפה אל SwiftUI באמצעות Coordinator
מה השלב הבא?
- Maps SDK ל-iOS
- התיעוד הרשמי של Maps SDK ל-iOS
- Places SDK ל-iOS – חיפוש עסקים מקומיים ונקודות עניין בסביבה שלכם
- maps-sdk-for-ios-samples
- קוד לדוגמה ב-GitHub שמדגים את כל התכונות ב-Maps SDK ל-iOS.
- SwiftUI – מסמכי התיעוד הרשמיים של Apple בנושא SwiftUI
- כדי לעזור לנו ליצור את התוכן שהכי יעזור לך, נבקש ממך למלא את הסקר הבא:
אילו codelabs נוספים היית רוצה לראות?
לא מוצאים את ה-codelab שהכי מעניין אתכם? כאן אפשר לשלוח בקשה בנושא בעיה חדשה.