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 חדש, צריך לעדכן את הפונקציה MapViewControllerBridgeupdateUIViewController_, 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 שהכי מעניין אתכם? כאן אפשר לשלוח בקשה בנושא בעיה חדשה.