1. לפני שמתחילים
בשיעור הזה תלמדו איך ליצור אפליקציית מפות תלת-ממד ב-SwiftUI באמצעות Maps 3D SDK ל-iOS.
תלמדו:
- איך שולטים במצלמה כדי להציג מיקומים ולעוף מעל המפה.
- איך מוסיפים סמנים ומודלים
- איך שרטטים קווים ופוליגונים
- איך מטפלים בקליקים של משתמשים על סמנים של מקומות.
דרישות מוקדמות
- פרויקט במסוף Google שבו החיוב מופעל
- מפתח API, שאפשר להגביל אותו ל-Maps 3D SDK ל-iOS.
- ידע בסיסי בפיתוח iOS באמצעות SwiftUI.
הפעולות שתבצעו:
- הגדרת Xcode והוספת ה-SDK באמצעות Swift Package Manager
- הגדרת האפליקציה לשימוש במפתח API
- הוספת מפה תלת-ממדית בסיסית לאפליקציה
- לשלוט במצלמה כדי לטוס למיקומים ספציפיים ולעוף סביבם
- הוספת סמנים, קווים, פוליגונים ומודלים למפה
מה נדרש
- Xcode מגרסה 15 ואילך.
2. להגדרה
בשלב ההפעלה הבא, תצטרכו להפעיל את Maps 3D SDK ל-iOS.
הגדרת הפלטפורמה של מפות Google
אם עדיין אין לכם חשבון ב-Google Cloud Platform ופרויקט שבו החיוב מופעל, תוכלו להיעזר במדריך תחילת העבודה עם פלטפורמת מפות Google כדי ליצור חשבון לחיוב ופרויקט.
- ב-Cloud Console, לוחצים על התפריט הנפתח של הפרויקט ובוחרים את הפרויקט שבו רוצים להשתמש ב-codelab הזה.
- מפעילים את ממשקי ה-API וערכות ה-SDK של הפלטפורמה של מפות Google הנדרשים לסדנת הקוד הזו ב-Google Cloud Marketplace. כדי לעשות זאת, פועלים לפי השלבים שמפורטים בסרטון הזה או במסמך הזה.
- יוצרים מפתח API בדף Credentials במסוף Cloud. אפשר לפעול לפי השלבים שמפורטים בסרטון הזה או במסמך הזה. כל הבקשות לפלטפורמה של מפות Google מחייבות מפתח API.
הפעלת Maps 3D SDK ל-iOS
אפשר למצוא את Maps 3D SDK ל-iOS באמצעות הקישור בתפריט Google Maps Platform > APIs and Services במסוף.
לוחצים על Enable כדי להפעיל את ה-API בפרויקט שנבחר.
3. יצירת אפליקציה בסיסית ב-SwiftUI
הערה: אפשר למצוא את קוד הפתרון לכל שלב במאגר של אפליקציית הדוגמה ב-codelab ב-GitHub .
יוצרים אפליקציה חדשה ב-Xcode.
הקוד של השלב הזה נמצא בתיקייה GoogleMaps3DDemo ב-GitHub.
פותחים את Xcode ויוצרים אפליקציה חדשה. מציינים את SwiftUI.
קוראים לאפליקציה GoogleMaps3DDemo
, עם שם חבילה com.example.GoogleMaps3DDemo
.
מייבאים את ספריית GoogleMaps3D לפרויקט
מוסיפים את ה-SDK לפרויקט באמצעות Swift Package Manager.
בפרויקט או בסביבת העבודה ב-Xcode, עוברים אל File (קובץ) > Add Package Dependencies (הוספת יחסי תלות בחבילות). מזינים את כתובת ה-URL https://github.com/googlemaps/ios-maps-3d-sdk, מקישים על Enter כדי לשלוח את החבילה ולוחצים על 'הוספת חבילה'.
בחלון Choose Package Products (בחירת מוצרים לחבילה), מוודאים ש-GoogleMaps3D
יתווסף ליעד הראשי שהגדרתם. בסיום, לוחצים על 'הוספת חבילה'.
כדי לאמת את ההתקנה, עוברים לחלונית 'כללי' של היעד. בתפריט Frameworks, Libraries and Embedded Content (מסגרות, ספריות ותוכן מוטמע) אמורים להופיע החבילות שהותקנו. אפשר גם לעיין בקטע Package Dependencies (יחסי תלות בחבילות) ב-Project Navigator כדי לאמת את החבילה ואת הגרסה שלה.
הוספת מפתח ה-API
אפשר להטמיע את מפתח ה-API באפליקציה, אבל זו לא שיטה מומלצת. הוספת קובץ תצורה מאפשרת לשמור את מפתח ה-API בסוד, ומונעת את הצורך לבצע עליו בדיקת קוד (check-in) במערכת בקרת הגרסאות.
יצירת קובץ תצורה חדש בתיקיית השורש של הפרויקט
ב-Xcode, מוודאים שמוצג חלון ה-Project Explorer. לוחצים לחיצה ימנית על שורש הפרויקט ובוחרים באפשרות 'קובץ חדש מתבנית'. גוללים עד שמגיעים לקטע 'Configuration Settings File'. בוחרים באפשרות הזו ולוחצים על 'הבא'. נותנים לקובץ את השם Config.xcconfig
ומוודאים שבחרתם בתיקיית השורש של הפרויקט. לוחצים על 'יצירה' כדי ליצור את הקובץ.
בעורך, מוסיפים שורה לקובץ התצורה באופן הבא: MAPS_API_KEY = YOUR_API_KEY
מחליפים את הערך YOUR_API_KEY
במפתח ה-API שלכם.
מוסיפים את ההגדרה הזו ל-Info.plist
.
כדי לעשות זאת, בוחרים את שורש הפרויקט ולוחצים על הכרטיסייה 'מידע'.
מוסיפים נכס חדש בשם MAPS_API_KEY
עם הערך $(MAPS_API_KEY)
.
בקוד האפליקציה לדוגמה יש קובץ Info.plist
שמציין את המאפיין הזה.
הוספת מפה
פותחים את הקובץ GoogleMaps3DDemoApp.swift
. זוהי נקודת הכניסה והניווט הראשי של האפליקציה.
הוא קורא ל-ContentView()
, שמציג את ההודעה Hello World.
פותחים את ContentView.swift
בעורך.
מוסיפים הצהרת import
עבור GoogleMaps3D
.
מוחקים את הקוד בתוך בלוק הקוד var body: some View {}
. מגדירים Map()
חדש בתוך body
.
ההגדרה המינימלית שצריך כדי לאתחל Map
היא MapMode
. יש לשדה הזה שני ערכים אפשריים:
.hybrid
– תמונות לוויין עם כבישים ותיאורי מקום, או.satellite
– תמונות לוויין בלבד.
הצוות של .hybrid
ישמח לתת לכם שירות.
קובץ ContentView.swift
אמור להיראות כך.
import GoogleMaps3D
import SwiftUI
@main
struct ContentView: View {
var body: some View {
Map(mode: .hybrid)
}
}
מגדירים את מפתח ה-API.
צריך להגדיר את מפתח ה-API לפני שמפעילים את המפה.
כדי לעשות זאת, מגדירים את Map.apiKey
בבורר האירועים init()
של כל View
שמכיל מפה. אפשר גם להגדיר אותו ב-GoogleMaps3DDemoApp.swift
לפני שהוא קורא ל-ContentView()
.
ב-GoogleMaps3DDemoApp.swift
, מגדירים את Map.apiKey
בגורם המטפל באירועים onAppear
של ה-WindowGroup
.
אחזור מפתח ה-API מקובץ התצורה
משתמשים ב-Bundle.main.infoDictionary
כדי לגשת להגדרה MAPS_API_KEY
שיצרתם בקובץ התצורה.
import GoogleMaps3D
import SwiftUI
@main
struct GoogleMaps3DDemoApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.onAppear {
guard let infoDictionary: [String: Any] = Bundle.main.infoDictionary else {
fatalError("Info.plist not found")
}
guard let apiKey: String = infoDictionary["MAPS_API_KEY"] as? String else {
fatalError("MAPS_API_KEY not set in Info.plist")
}
Map.apiKey = apiKey
}
}
}
מפתחים ומפעילים את האפליקציה כדי לבדוק שהיא נטענת כמו שצריך. אמורה להופיע מפה של העולם.
4. שימוש במצלמה כדי לשלוט בתצוגת המפה
יצירת אובייקט של מצב המצלמה
תצוגות המפה בתלת-ממד נשלטות על ידי הכיתה Camera
. בשלב הזה תלמדו לציין את המיקום, הגובה, כיוון המצפן, הטיה, תנועת רוטציה והטווח כדי להתאים אישית את תצוגת המפה.
יצירת כיתה Helpers לאחסון הגדרות המצלמה
מוסיפים קובץ ריק חדש בשם MapHelpers.swift
. בקובץ החדש, מייבאים את GoogleMaps3D
ומוסיפים תוסף לכיתה Camera
. מוסיפים משתנה בשם sanFrancisco
. מאתחלים את המשתנה הזה כאובייקט Camera
חדש. ממקמים את המצלמה ב-latitude: 37.39, longitude: -122.08
.
import GoogleMaps3D
extension Camera {
public static var sanFrancisco: Camera = .init(latitude: 37.39, longitude: -122.08)
}
הוספת תצוגה מפורטת חדשה לאפליקציה
יוצרים קובץ חדש בשם CameraDemo.swift
. מוסיפים לקובץ את המתאר הבסיסי של תצוגה חדשה של SwiftUI.
מוסיפים משתנה @State
בשם camera
מסוג Camera
. מאתחלים אותו למצלמה sanFrancisco
שהגדרתם.
השימוש ב-@State
מאפשר לקשר את המפה למצב המצלמה ולהשתמש בה כמקור האמיתי.
@State var camera: Camera = .sanFrancisco
משנים את קריאת הפונקציה Map()
כך שתכלול את המאפיין camera
. משתמשים בקישור המצב של המצלמה $camera
כדי לאתחל את המאפיין camera
באובייקט @State
של המצלמה (.sanFrancisco
).
import SwiftUI
import GoogleMaps3D
struct CameraDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid)
}
}
}
הוספת ממשק משתמש בסיסי לניווט לאפליקציה
מוסיפים NavigationView
לנקודת הכניסה הראשית של האפליקציה, GoogleMaps3DDemoApp.swift
.
כך המשתמשים יוכלו לראות רשימה של הדגמות וללחוץ על כל אחת מהן כדי לפתוח אותה.
עורכים את GoogleMaps3DDemoApp.swift
כדי להוסיף NavigationView
חדש.
מוסיפים List
שמכיל שתי הצהרות NavigationLink
.
ה-NavigationLink
הראשון אמור לפתוח את ContentView()
עם תיאור Text
Basic Map
.
ה-NavigationLink
השני אמור לפתוח את CameraDemo()
.
...
NavigationView {
List {
NavigationLink(destination: ContentView()) {
Text("Basic Map")
}
NavigationLink(destination: CameraDemo()) {
Text("Camera Demo")
}
}
}
...
הוספת תצוגה מקדימה של Xcode
תצוגות מקדימות הן תכונה חזקה של Xcode שמאפשרת לכם לראות את האפליקציה ולבצע בה פעולות בזמן שאתם מבצעים בה שינויים.
כדי להוסיף תצוגה מקדימה, פותחים את CameraDemo.swift
. מוסיפים בלוק קוד של #Preview {}
מחוץ ל-struct
.
#Preview {
CameraDemo()
}
פותחים או מרעננים את חלונית התצוגה המקדימה ב-Xcode. במפה אמורה להופיע סן פרנסיסקו.
הגדרה של תצוגות תלת-ממדיות בהתאמה אישית
אפשר לציין פרמטרים נוספים כדי לשלוט במצלמה:
heading
: הכיוון במעלות מצפון שאליו מכוונים את המצלמה.tilt
: זווית ההטיה במעלות, כאשר 0 היא ישירות מעל הראש ו-90 היא צפייה אופקית.roll
: זווית הנטייה סביב המטוס האנכי של המצלמה, במעלותrange
: המרחק במטרים של המצלמה מהמיקום לפי קו הרוחב וקו האורךaltitude
: הגובה של המצלמה מעל פני הים
אם לא תספקו אף אחד מהפרמטרים הנוספים האלה, המערכת תשתמש בערכי ברירת המחדל.
כדי להציג יותר נתוני תלת-ממד בתצוגת המצלמה, מגדירים את הפרמטרים הראשוניים כך שיציגו תצוגה קרובה יותר ומשופעת.
עורכים את Camera
שהגדרתם ב-MapHelpers.swift
כך שיכלול ערכים עבור altitude
, heading
, tilt
, roll
ו-range
public static var sanFrancisco: Camera = .init(
latitude: 37.7845812,
longitude: -122.3660241,
altitude: 585,
heading: 288.0,
tilt: 75.0,
roll: 0.0,
range: 100)
מפתחים ומפעילים את האפליקציה כדי לראות ולחקור את התצוגה החדשה בתלת-ממד.
5. אנימציות בסיסיות של מצלמה
עד עכשיו השתמשתם במצלמה כדי לציין מיקום יחיד עם הטיה, גובה, כיוון וטווח. בשלב הזה תלמדו איך להזיז את תצוגת המצלמה באמצעות אנימציה של המאפיינים האלה ממצב ראשוני למצב חדש.
מעבר בטיסה למיקום מסוים
משתמשים בשיטה Map.flyCameraTo()
כדי ליצור אנימציה של המצלמה מהמיקום הראשוני למיקום חדש.
השיטה flyCameraTo()
מקבלת מספר פרמטרים:
Camera
שמייצג את מיקום הסיום.duration
: משך הזמן שבו האנימציה תפעל, בשניות.trigger
: אובייקט שניתן לצפות בו, שיפעיל את האנימציה כשהסטטוס שלו ישתנה.completion
: קוד שיתבצע בסיום האנימציה.
הגדרת מיקום לטיסה
פותחים את קובץ ה-MapHelpers.swift
.
מגדירים אובייקט מצלמה חדש כדי להציג את סיאטל.
public static var seattle: Camera = .init(latitude:
47.6210296,longitude: -122.3496903, heading: 149.0, tilt: 77.0, roll: 0.0, range: 4000)
מוסיפים לחצן כדי להפעיל את ההנפשה.
פותחים את CameraDemo.swift
. מגדירים משתנה בוליאני חדש בתוך struct
.
נקרא לו animate
עם ערך ראשוני של false
.
@State private var animate: Bool = false
מוסיפים Button
מתחת ל-VStack
. ה-Button
יפעיל את האנימציה של המפה.
נותנים ל-Button
Text
מתאים, למשל 'התחלת הטיסה'.
import SwiftUI
import GoogleMaps3D
struct CameraDemo: View {
@State var camera:Camera = .sanFrancisco
@State private var animate: Bool = false
var body: some View {
VStack{
Map(camera: $camera, mode: .hybrid)
Button("Start Flying") {
}
}
}
}
ב-Button closure מוסיפים קוד כדי להחליף את המצב של המשתנה animate
.
Button("Start Flying") {
animate.toggle()
}
מפעילים את האנימציה.
מוסיפים את הקוד כדי להפעיל את האנימציה flyCameraTo()
כשהמצב של המשתנה animate
משתנה.
var body: some View {
VStack{
Map(camera: $camera, mode: .hybrid)
.flyCameraTo(
.seattle,
duration: 5,
trigger: animate,
completion: { }
)
Button("Start Flying") {
animate.toggle()
}
}
}
לטוס סביב מיקום
אפשר להשתמש בשיטה Map.flyCameraAround()
כדי לעוף סביב מיקום מסוים. לשיטה הזו יש כמה פרמטרים:
Camera
שמגדיר את המיקום והתצוגה.duration
בשניות.rounds
: מספר הפעמים שרוצים לחזור על האנימציה.trigger
: אובייקט שניתן לצפות בו שיפעיל את האנימציה.callback
: קוד שיופעל כשהאנימציה תרוץ.
מגדירים משתנה @State
חדש בשם flyAround
, עם ערך ראשוני של false
.
לאחר מכן, מוסיפים קריאה ל-flyCameraAround()
מיד אחרי קריאת השיטה flyCameraTo()
.
משך הטיסה צריך להיות ארוך יחסית כדי שהתצוגה תשתנה בצורה חלקה.
חשוב להפעיל את האנימציה flyCameraAround()
על ידי שינוי המצב של אובייקט הטריגר כשהפעולה flyCameraTo()
מסתיימת.
הקוד אמור להיראות כך.
import SwiftUI
import GoogleMaps3D
struct CameraDemo: View {
@State var camera:Camera = .sanFrancisco
@State private var animate: Bool = false
@State private var flyAround: Bool = false
var body: some View {
VStack{
Map(camera: $camera, mode: .hybrid)
.flyCameraTo(
.seattle,
duration: 5,
trigger: animate,
completion: { flyAround = true }
)
.flyCameraAround(
.seattle,
duration: 15,
rounds: 0.5,
trigger: flyAround,
callback: { }
)
Button("Start Flying") {
animate.toggle()
}
}
}
}
#Preview {
CameraDemo()
}
בודקים את התצוגה המקדימה של האפליקציה או מפעילים אותה כדי לראות שהמצלמה טסה סביב היעד אחרי שהאנימציה flyCameraTo()
מסתיימת.
6. מוסיפים סמן למפה.
בשלב הזה תלמדו איך לצייר סיכה במפה.
תיצורו אובייקט Marker
ותוסיפו אותו למפה. ערכת ה-SDK תשתמש בסמל ברירת מחדל לסמן. בסיום, תשנו את הגובה של הסמן ומאפיינים אחרים כדי לשנות את אופן התצוגה שלו.
יוצרים תצוגה חדשה של SwiftUI לדמו של Marker.
מוסיפים קובץ Swift חדש לפרויקט. נקרא לה MarkerDemo.swift
.
מוסיפים את המתאר של תצוגת SwiftUI ומפעילים את המפה כמו שעשיתם ב-CameraDemo
.
import SwiftUI
import GoogleMaps3D
struct MarkerDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid)
}
}
}
איך מפעילים אובייקט Marker
מגדירים משתנה סמן חדש בשם mapMarker
. בחלק העליון של בלוק הקוד struct
ב-MarkerDemo.swift
.
מציבים את ההגדרה בשורה שמתחת להצהרה camera
. קוד לדוגמה שמאפס את כל הנכסים הזמינים.
@State var mapMarker: Marker = .init(
position: .init(
latitude: 37.8044862,
longitude: -122.4301493,
altitude: 0.0),
altitudeMode: .absolute,
collisionBehavior: .required,
extruded: false,
drawsWhenOccluded: true,
sizePreserved: true,
zIndex: 0,
label: "Test"
)
מוסיפים את הסמן למפה.
כדי לצייר את הסמן, מוסיפים אותו לחסימה שנקראת כשהמפה נוצרת.
struct MarkerDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
mapMarker
}
}
}
}
מוסיפים NavigationLink
חדש ל-GoogleMaps3DDemoApp.swift
עם יעד MarkerDemo()
ו-Text
ומתארים אותו בתור 'דוגמה לסמן'.
...
NavigationView {
List {
NavigationLink(destination: Map()) {
Text("Basic Map")
}
NavigationLink(destination: CameraDemo()) {
Text("Camera Demo")
}
NavigationLink(destination: MarkerDemo()) {
Text("Marker Demo")
}
}
}
...
תצוגה מקדימה והפעלה של האפליקציה
מרעננים את התצוגה המקדימה או מריצים את האפליקציה כדי לראות את הסמן.
סמנים מורחבים
אפשר להציב סמנים מעל הקרקע או מעל רשת התלת-מימד באמצעות המקשים altitude
ו-altitudeMode
.
מעתיקים את ההצהרה על mapMarker
ב-MarkerDemo.swift
למשתנה Marker
חדש שנקרא extrudedMarker
.
מגדירים ערך שאינו אפס עבור altitude
, 50 מספיק.
משנים את הערך של altitudeMode
ל-.relativeToMesh
ומגדירים את extruded
ל-true
. כדי למקם את הסמן בראש גורד שחקים, משתמשים ב-latitude
וב-longitude
מקטע הקוד הזה.
@State var extrudedMarker: Marker = .init(
position: .init(
latitude: 37.78980534,
longitude: -122.3969349,
altitude: 50.0),
altitudeMode: .relativeToMesh,
collisionBehavior: .required,
extruded: true,
drawsWhenOccluded: true,
sizePreserved: true,
zIndex: 0,
label: "Extruded"
)
מריצים או מציגים תצוגה מקדימה של האפליקציה שוב. הסמן אמור להופיע מעל בניין תלת-ממדי.
7. מוסיפים מודל למפה.
אפשר להוסיף Model
באותו אופן שבו מוסיפים Marker
. תצטרכו קובץ מודל שאפשר לגשת אליו באמצעות כתובת URL או להוסיף כקובץ מקומי בפרויקט. בשלב הזה נשתמש בקובץ מקומי שאפשר להוריד מהמאגר ב-GitHub של סדנת הקוד הזו.
הוספת קובץ מודל לפרויקט
יוצרים תיקייה חדשה בשם Models
בפרויקט Xcode.
מורידים את המודל ממאגר האפליקציות לדוגמה ב-GitHub. מוסיפים אותו לפרויקט על ידי גרירה לתיקייה החדשה בתצוגת הפרויקט ב-Xcode.
חשוב לוודא שהגדרתם את היעד הזה כיעד הראשי של האפליקציה.
בודקים את ההגדרות של Build Phases > Copy Bundle Resources בפרויקט. קובץ המודל צריך להופיע ברשימת המשאבים שהועברו לחבילה. אם היא לא מופיעה, לוחצים על '+' כדי להוסיף אותה.
מוסיפים את המודל לאפליקציה.
יוצרים קובץ SwiftUI חדש בשם ModelDemo.swift
.
מוסיפים הצהרות import
עבור SwiftUI
ו-GoogleMaps3D
כמו בשלבים הקודמים.
מגדירים Map
בתוך VStack
ב-body
.
import SwiftUI
import GoogleMaps3D
struct ModelDemo: View {
@State var camera: Camera = .sanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
}
}
}
}
מקבלים את נתיב המודל מה-Bundle. מוסיפים את הקוד הזה מחוץ ל-struct
.
private let fileUrl = Bundle.main.url(forResource: "balloon", withExtension: "glb")
מגדירים משתנה למודל בתוך המבנה.
מציינים ערך ברירת מחדל למקרה שלא יצוין ערך ל-fileUrl
.
@State var balloonModel: Model = .init(
position: .init(
latitude: 37.791376,
longitude: -122.397571,
altitude: 300.0),
url: URL(fileURLWithPath: fileUrl?.relativePath ?? ""),
altitudeMode: .absolute,
scale: .init(x: 5, y: 5, z: 5),
orientation: .init(heading: 0, tilt: 0, roll: 0)
)
3. משתמשים במודל במפה.
בדומה להוספת Marker
, פשוט מספקים את ההפניה ל-Model
בהצהרה Map
.
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
balloonModel
}
}
}
תצוגה מקדימה והפעלה של האפליקציה
מוסיפים NavigationLink
חדש ל-GoogleMaps3DDemoApp.swift
, עם יעד ModelDemo()
ו-Text
'דוגמה לדגם'.
...
NavigationLink(destination: ModelDemo()) {
Text("Model Demo")
}
...
מרעננים את התצוגה המקדימה או מריצים את האפליקציה כדי לראות את המודל.
8. מציירים קו ופוליגון במפה.
בשלב הזה תלמדו איך להוסיף קווים וצורות של פוליגונים למפה תלת-ממדית.
כדי לפשט את התהליך, נגדיר את הצורות כמערכי אובייקטים מסוג LatLngAltitude
. באפליקציה אמיתית, יכול להיות שהנתונים יוטמעו מקובץ, מקריאה ל-API או ממסד נתונים.
יוצרים כמה אובייקטים של צורות כדי לנהל את נתוני הצורות.
מוסיפים הגדרה חדשה של Camera
ל-MapHelpers.swift
שמתייחסת לדאונטאון של סן פרנסיסקו.
public static var downtownSanFrancisco: Camera = .init(latitude: 37.7905, longitude: -122.3989, heading: 25, tilt: 71, range: 2500)
מוסיפים קובץ חדש בשם ShapesDemo.swift
לפרויקט. מוסיפים struct
שנקרא ShapesDemo
שמטמיע את פרוטוקול View
, ומוסיפים לו body
.
struct ShapesDemo: View {
@State var camera: Camera = .downtownSanFrancisco
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
}
}
}
}
הכיתות שבהן משתמשים לניהול נתוני הצורות הן Polyline
ו-Polygon
. פותחים את ShapesDemo.swift
ומוסיפים אותם ל-struct
באופן הבא.
var polyline: Polyline = .init(coordinates: [
LatLngAltitude(latitude: 37.80515638571346, longitude: -122.4032569467164, altitude: 0),
LatLngAltitude(latitude: 37.80337073509504, longitude: -122.4012878349353, altitude: 0),
LatLngAltitude(latitude: 37.79925208843463, longitude: -122.3976697250461, altitude: 0),
LatLngAltitude(latitude: 37.7989102378512, longitude: -122.3983408725656, altitude: 0),
LatLngAltitude(latitude: 37.79887832784348, longitude: -122.3987094864192, altitude: 0),
LatLngAltitude(latitude: 37.79786443410338, longitude: -122.4066878788802, altitude: 0),
LatLngAltitude(latitude: 37.79549248916587, longitude: -122.4032992702785, altitude: 0),
LatLngAltitude(latitude: 37.78861484290265, longitude: -122.4019489189814, altitude: 0),
LatLngAltitude(latitude: 37.78618687561075, longitude: -122.398969592545, altitude: 0),
LatLngAltitude(latitude: 37.7892310309145, longitude: -122.3951458683092, altitude: 0),
LatLngAltitude(latitude: 37.7916358762409, longitude: -122.3981969390652, altitude: 0)
])
.stroke(GoogleMaps3D.Polyline.StrokeStyle(
strokeColor: UIColor(red: 0.09803921568627451, green: 0.403921568627451, blue: 0.8235294117647058, alpha: 1),
strokeWidth: 10.0,
outerColor: .white,
outerWidth: 0.2
))
.contour(GoogleMaps3D.Polyline.ContourStyle(isGeodesic: true))
var originPolygon: Polygon = .init(outerCoordinates: [
LatLngAltitude(latitude: 37.79165766856578, longitude: -122.3983762901255, altitude: 300),
LatLngAltitude(latitude: 37.7915324439261, longitude: -122.3982171091383, altitude: 300),
LatLngAltitude(latitude: 37.79166617650914, longitude: -122.3980478493319, altitude: 300),
LatLngAltitude(latitude: 37.79178986470217, longitude: -122.3982041104199, altitude: 300),
LatLngAltitude(latitude: 37.79165766856578, longitude: -122.3983762901255, altitude: 300 )
],
altitudeMode: .relativeToGround)
.style(GoogleMaps3D.Polygon.StyleOptions(fillColor:.green, extruded: true) )
var destinationPolygon: Polygon = .init(outerCoordinates: [
LatLngAltitude(latitude: 37.80515661739527, longitude: -122.4034307490334, altitude: 300),
LatLngAltitude(latitude: 37.80503794515428, longitude: -122.4032633416024, altitude: 300),
LatLngAltitude(latitude: 37.80517850164195, longitude: -122.4031056058006, altitude: 300),
LatLngAltitude(latitude: 37.80529346901115, longitude: -122.4032622466595, altitude: 300),
LatLngAltitude(latitude: 37.80515661739527, longitude: -122.4034307490334, altitude: 300 )
],
altitudeMode: .relativeToGround)
.style(GoogleMaps3D.Polygon.StyleOptions(fillColor:.red, extruded: true) )
שימו לב לפרמטרים של האיפוס שנעשה בהם שימוש.
altitudeMode: .relativeToGround
משמש להוצאה (extrude) של הפוליגונים לגובה ספציפי מעל הקרקע.- השדה
altitudeMode: .clampToGround
משמש כדי לגרום לקו הפוליגון לפעול בהתאם לצורה של פני כדור הארץ. - הסגנונות מוגדרים באובייקטים
Polygon
על ידי קישור של קריאה ל-methodstyleOptions()
אחרי הקריאה ל-.init()
הוספת הצורות למפה
בדומה לשלבים הקודמים, אפשר להוסיף את הצורות ישירות לחסימה Map
. יוצרים את ה-Map
בתוך VStack
.
...
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid) {
polyline
originPolygon
destinationPolygon
}
}
}
...
תצוגה מקדימה והפעלה של האפליקציה
מוסיפים קוד תצוגה מקדימה ובודקים את האפליקציה בחלונית התצוגה המקדימה ב-Xcode.
#Preview {
ShapesDemo()
}
כדי להריץ את האפליקציה, מוסיפים NavigationLink
חדש ל-GoogleMaps3DDemoApp.swift
שפותח את תצוגת הדגמה החדשה.
...
NavigationLink(destination: ShapesDemo()) {
Text("Shapes Demo")
}
...
מריצים את האפליקציה ומעיינים בצורות שהוספתם.
9. טיפול באירועי הקשה על סמנים של מקומות
בשלב הזה תלמדו איך להגיב להקשות של משתמשים על סמנים של מקומות.
הערה: כדי לראות את סמלי המיקומים במפה, צריך להגדיר את MapMode
כ-.hybrid
.
כדי לטפל בהקשה, צריך להטמיע את השיטה Map.onPlaceTap
.
האירוע onPlaceTap
מספק אובייקט PlaceTapInfo
שממנו אפשר לקבל את מזהה המקום של סמן המקום שנלחץ עליו.
אפשר להשתמש במזהה המקום כדי לחפש פרטים נוספים באמצעות Places SDK או Places API.
הוספת תצוגה מפורטת חדשה של Swift
מוסיפים את הקוד הבא לקובץ Swift חדש בשם PlaceTapDemo.swift
.
import GoogleMaps3D
import SwiftUI
struct PlaceTapDemo: View {
@State var camera: Camera = .sanFrancisco
@State var isPresented = false
@State var tapInfo: PlaceTapInfo?
var body: some View {
Map(camera: $camera, mode: .hybrid)
.onPlaceTap { tapInfo in
self.tapInfo = tapInfo
isPresented.toggle()
}
.alert(
"Place tapped - \(tapInfo?.placeId ?? "nil")",
isPresented: $isPresented,
actions: { Button("OK") {} }
)
}
}
#Preview {
PlaceTapDemo()
}
תצוגה מקדימה והפעלה של האפליקציה
פותחים את חלונית התצוגה המקדימה כדי לראות תצוגה מקדימה של האפליקציה.
כדי להריץ את האפליקציה, מוסיפים NavigationLink
חדש ל-GoogleMaps3DDemoApp.swift
.
...
NavigationLink(destination: PlaceTapDemo()) {
Text("Place Tap Demo")
}
...
10. (אופציונלי) המשך התהליך
אנימציות מתקדמות במצלמה
בתרחישי שימוש מסוימים נדרשת אנימציה חלקה לאורך רצף או רשימה של מיקומים או מצבי מצלמה, למשל בסימולטור טיסה או בהפעלה חוזרת של טיול או ריצה.
בשלב הזה תלמדו איך לטעון רשימה של מיקומים מקובץ, ולהציג אנימציה של כל מיקום ברצף.
טעינת קובץ שמכיל רצף של מיקומים.
מורידים את flightpath.json
ממאגר האפליקציות לדוגמה ב-GitHub.
יוצרים תיקייה חדשה בשם JSON
בפרויקט Xcode.
גוררים את flightpath.json
לתיקייה JSON
ב-Xcode.
מגדירים את היעד הזה כיעד הראשי של האפליקציה. בודקים שהקובץ הזה נכלל בהגדרות של Copy Bundle Resources בפרויקט.
יוצרים באפליקציה שני קובצי Swift חדשים בשם FlightPathData.swift
ו-FlightDataLoader.swift
.
מעתיקים את הקוד הבא לאפליקציה. הקוד הזה יוצר מבנים וסוגי נתונים (classes) שקוראים קובץ מקומי שנקרא 'flighpath.json' ומפענחים אותו כ-JSON.
המבנים FlightPathData
ו-FlightPathLocation
מייצגים את מבנה הנתונים בקובץ ה-JSON כאובייקטים של Swift.
הכיתה FlightDataLoader
קוראת את הנתונים מהקובץ ומפענחת אותם. הוא משתמש בפרוטוקול ObservableObject
כדי לאפשר לאפליקציה לעקוב אחרי השינויים בנתונים שלה.
הנתונים המנותחים נחשפים דרך נכס שפורסם.
FlightPaths.swift
import GoogleMaps3D
struct FlightPathData: Decodable {
let flight: [FlightPathLocation]
}
struct FlightPathLocation: Decodable {
let timestamp: Int64
let latitude: Double
let longitude: Double
let altitude: Double
let bearing: Double
let speed: Double
}
FlightDataLoader.swift
import Foundation
public class FlightDataLoader : ObservableObject {
@Published var flightPathData: FlightPathData = FlightPathData(flight:[])
@Published var isLoaded: Bool = false
public init() {
load("flightpath.json")
}
public func load(_ path: String) {
if let url = Bundle.main.url(forResource: path, withExtension: nil){
if let data = try? Data(contentsOf: url){
let jsondecoder = JSONDecoder()
do{
let result = try jsondecoder.decode(FlightPathData.self, from: data)
flightPathData = result
isLoaded = true
}
catch {
print("Error trying to load or parse the JSON file.")
}
}
}
}
}
הנפשת המצלמה לאורך כל מיקום
כדי להציג אנימציה של המצלמה בין רצף של שלבים, משתמשים ב-KeyframeAnimator
.
כל Keyframe
ייוצר כ-CubicKeyframe
, כך שהשינויים במצב המצלמה יוצגו באנימציה חלקה.
שימוש ב-flyCameraTo()
יגרום לתצוגה "להקפץ" בין כל מיקום.
קודם כול מגדירים מצלמה בשם 'innsbruck' ב-MapHelpers.swift
.
public static var innsbruck: Camera = .init(
latitude: 47.263,
longitude: 11.3704,
altitude: 640.08,
heading: 237,
tilt: 80.0,
roll: 0.0,
range: 200)
עכשיו מגדירים תצוגה חדשה בקובץ חדש בשם FlyAlongRoute.swift
.
מייבאים את SwiftUI
ואת GoogleMaps3D
. מוסיפים Map
ו-Button
בתוך VStack
. מגדירים את Button
כדי להחליף את המצב של המשתנה הבוליאני animation
.
מגדירים אובייקט State
עבור FlightDataLoader
, שיטען את קובץ ה-JSON כשהוא יופעל.
import GoogleMaps3D
import SwiftUI
struct FlyAlongRoute: View {
@State private var camera: Camera = .innsbruck
@State private var flyToDuration: TimeInterval = 5
@State var animation: Bool = true
@StateObject var flightData: FlightDataLoader = FlightDataLoader()
var body: some View {
VStack {
Map(camera: $camera, mode: .hybrid)
Button("Fly Along Route"){
animation.toggle()
}
}
}
}
יצירת הפריימים המרכזיים
התהליך הבסיסי הוא ליצור פונקציה שמחזירה פריים חדש בסדרת האנימציה. כל פריים חדש מגדיר את מצב המצלמה הבא שהאנימטור צריך ליצור עבורו אנימציה. אחרי שיוצרים את הפונקציה הזו, צריך להפעיל אותה עם כל מיקום מהקובץ ברצף.
מוסיפים שתי פונקציות למבנה FlyAlongRoute
. הפונקציה makeKeyFrame
מחזירה CubicKeyframe
עם מצב מצלמה. הפונקציה makeCamera
מקבלת שלב ברצף נתוני הטיסה ומחזירה אובייקט Camera
שמייצג את השלב.
func makeKeyFrame(step: FlightPathLocation) -> CubicKeyframe<Camera> {
return CubicKeyframe(
makeCamera(step: step),
duration: flyToDuration
)
}
func makeCamera(step: FlightPathLocation) -> Camera {
return .init(
latitude: step.latitude,
longitude: step.longitude,
altitude: step.altitude,
heading: step.bearing,
tilt: 75,
roll: 0,
range: 200
)
}
איך משלבים את האנימציה
קוראים ל-keyframeAnimator
אחרי האיניציאליזציה של Map
ומגדירים את הערכים הראשוניים.
תצטרכו מצב מצלמה ראשוני שמבוסס על המיקום הראשון בנתיב הטיסה.
האנימציה צריכה להופעל על סמך מצב משתנה שמשתנה.
התוכן של keyframeAnimator
צריך להיות מפה.
רשימת ה-keyframes בפועל נוצרת על ידי חזרה על כל מיקום בנתיב הטיסה.
VStack {
Map(camera: $camera, mode: .hybrid)
.keyframeAnimator(
initialValue: makeCamera(step: flightData.flightPathData.flight[0]),
trigger: animation,
content: { view, value in
Map(camera: .constant(value), mode: .hybrid)
},
keyframes: { _ in
KeyframeTrack(content: {
for i in 1...flightData.flightPathData.flight.count-1 {
makeKeyFrame(step: flightData.flightPathData.flight[i])
}
})
}
)
}
מציגים תצוגה מקדימה של האפליקציה ומריצים אותה.
פותחים את חלונית התצוגה המקדימה כדי לראות תצוגה מקדימה של התצוגה.
מוסיפים NavigationLink
חדש עם יעד FlightPathDemo()
אל GoogleMaps3DDemoApp.swift
ומריצים את האפליקציה כדי לנסות אותה.
11. מזל טוב
יצרתם אפליקציה ש:
- הוספת מפה תלת-ממדית בסיסית לאפליקציה.
- הוספת סמנים, קווים, פוליגונים ומודלים למפה.
- הטמעת קוד לשליטה במצלמה כדי לעבור מעל המפה וסביב מיקומים ספציפיים.
מה למדתם
- איך מוסיפים את החבילה
GoogleMaps3D
לאפליקציית Xcode SwiftUI. - איך מאתחלים מפה תלת-ממדית באמצעות מפתח API ותצוגת ברירת מחדל.
- איך מוסיפים למפה סמנים, מודלים תלת-ממדיים, קווים ופוליגונים.
- איך לשלוט במצלמה כדי ליצור אנימציה של תנועה למיקום אחר.
- איך מטפלים באירועי קליקים על סמני מיקום.
מה השלב הבא?
- במדריך למפתחים מפורט מה אפשר לעשות באמצעות Maps 3D SDK ל-iOS.
- כדי שנוכל ליצור את התוכן הכי שימושי עבורך, נשמח לקבל ממך תשובות לסקר הבא: