1. قبل البدء
يشرح لك هذا الدرس التطبيقي حول الترميز كيفية إنشاء تطبيق خرائط ثلاثية الأبعاد في SwiftUI باستخدام حزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" الثلاثية الأبعاد لأجهزة iOS.
ستتعرّف على:
- كيفية التحكّم في الكاميرا لعرض المواقع الجغرافية والتنقّل حول الخريطة
- كيفية إضافة العلامات والنماذج
- كيفية رسم الخطوط والمضلّعات
- كيفية التعامل مع نقرات المستخدمين على علامات الأماكن
المتطلبات الأساسية
- مشروع على Google Console تم تفعيل ميزة الفوترة فيه
- مفتاح واجهة برمجة التطبيقات، الذي يمكن اختياريًا حصر استخدامه بحزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" الثلاثية الأبعاد لأجهزة iOS
- معرفة أساسية بتطوير تطبيقات iOS باستخدام SwiftUI
الإجراءات التي ستنفذّها
- إعداد Xcode واستخدام Swift Package Manager لإدخال حزمة تطوير البرامج (SDK)
- ضبط تطبيقك لاستخدام مفتاح واجهة برمجة التطبيقات
- إضافة خريطة ثلاثية الأبعاد أساسية إلى تطبيقك
- التحكّم في الكاميرا للانتقال إلى مواقع جغرافية معيّنة والتنقّل حولها
- إضافة علامات وخطوط ومضلّعات ونماذج إلى خريطتك
المتطلبات
- الإصدار 15 من Xcode أو إصدار أحدث
2. طريقة الإعداد
في خطوة التفعيل التالية، عليك تفعيل حزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" الثلاثية الأبعاد لأجهزة iOS.
إعداد "منصة خرائط Google"
إذا لم يكن لديك حساب على Google Cloud Platform ومشروع تم تفعيل ميزة الفوترة فيه، يُرجى الاطّلاع على دليل البدء في استخدام Google Maps Platform لإنشاء حساب فوترة ومشروع.
- في Cloud Console، انقر على القائمة المنسدلة للمشروع واختَر المشروع الذي تريد استخدامه في هذا الدليل التعليمي.
- فعِّل واجهات برمجة تطبيقات Google Maps Platform وحِزم SDK المطلوبة لهذا الدليل التعليمي في Google Cloud Marketplace. لإجراء ذلك، اتّبِع الخطوات الواردة في هذا الفيديو أو هذه المستندات.
- أنشئ مفتاح واجهة برمجة التطبيقات في صفحة بيانات الاعتماد في Cloud Console. يمكنك اتّباع الخطوات الواردة في هذا الفيديو أو هذه المستندات. تتطلّب جميع الطلبات المرسَلة إلى "منصّة خرائط Google" مفتاح واجهة برمجة التطبيقات.
تفعيل حزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" الثلاثية الأبعاد لأجهزة iOS
يمكنك العثور على حزمة تطوير البرامج (SDK) لميزة "التجوّل الافتراضي" في "خرائط Google" لنظام التشغيل iOS باستخدام رابط قائمة "منصة خرائط Google" > واجهات برمجة التطبيقات والخدمات في وحدة التحكّم.
انقر على "تفعيل" لتفعيل واجهة برمجة التطبيقات في المشروع الذي اخترته.
3- إنشاء تطبيق SwiftUI أساسي
ملاحظة: يمكنك العثور على رمز الحل لكل خطوة في مستودع تطبيق نموذج دورة codelab على GitHub .
أنشئ تطبيقًا جديدًا في Xcode.
يمكن العثور على الرمز البرمجي لهذه الخطوة في المجلد GoogleMaps3DDemo على GitHub.
افتح Xcode وأنشئ تطبيقًا جديدًا. حدِّد SwiftUI.
اضبط اسم تطبيقك على GoogleMaps3DDemo
واضبط اسم الحزمة على com.example.GoogleMaps3DDemo
.
استيراد مكتبة GoogleMaps3D إلى مشروعك
أضِف حزمة تطوير البرامج (SDK) إلى مشروعك باستخدام أداة Swift Package Manager.
في مشروع Xcode أو مساحة العمل، انتقِل إلى ملف > إضافة تبعيات الحزمة. أدخِل https://github.com/googlemaps/ios-maps-3d-sdk كعنوان URL، واضغط على مفتاح Enter لسحب الحزمة، ثم انقر على "إضافة حزمة".
من نافذة "اختيار منتجات الحزمة"، تأكَّد من أنّه سيتمّ إضافة GoogleMaps3D
إلى الاستهداف الرئيسي المحدّد. بعد الانتهاء، انقر على "إضافة حزمة".
للتحقّق من عملية التثبيت، انتقِل إلى اللوحة "العامة" لهدفك. في "الإطارات الأساسية" و"المكتبات" و"المحتوى المضمّن"، من المفترض أن تظهر الحِزم المثبَّتة. يمكنك أيضًا الاطّلاع على قسم "تبعيات الحزمة" في "مستكشف المشاريع" للتحقّق من الحزمة وإصدارها.
إضافة مفتاح واجهة برمجة التطبيقات
يمكنك تضمين مفتاح واجهة برمجة التطبيقات في التطبيق بشكلٍ ثابت، ولكن هذه ليست ممارسة جيدة. تتيح لك إضافة ملفّ ضبط الاحتفاظ بسرية مفتاح واجهة برمجة التطبيقات وتجنُّب التحقّق منه في أداة التحكّم في المصدر.
أنشئ ملف إعدادات جديدًا في المجلد الجذر للمشروع.
في Xcode، تأكَّد من أنّك تطّلع على نافذة "مستكشف المشاريع". انقر بزر الماوس الأيمن على جذر المشروع واختَر "ملف جديد من نموذج". انتقِل للأسفل إلى أن يظهر لك "ملف إعدادات الضبط". اختَر هذا الخيار وانقر على "التالي". أدخِل اسم الملف Config.xcconfig
وتأكَّد من اختيار المجلد الجذر للمشروع. انقر على "إنشاء" لإنشاء الملف.
في المحرِّر، أضِف سطرًا إلى ملف الإعدادات على النحو التالي: MAPS_API_KEY = YOUR_API_KEY
استبدِل YOUR_API_KEY
بمفتاح واجهة برمجة التطبيقات.
أضِف هذا الإعداد إلى Info.plist
.
لإجراء ذلك، اختَر جذر المشروع وانقر على علامة التبويب "معلومات".
أضِف سمة جديدة باسم MAPS_API_KEY
وقيمة $(MAPS_API_KEY)
.
يحتوي نموذج رمز التطبيق على ملف Info.plist
يحدِّد هذه السمة.
إضافة خريطة
افتح الملف الذي يُسمى GoogleMaps3DDemoApp.swift
. هذه هي نقطة الدخول وشريط التنقّل الرئيسي في تطبيقك.
يتصل بـ ContentView()
الذي يعرض رسالة "مرحبًا".
افتح 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)
}
}
اضبط مفتاح واجهة برمجة التطبيقات.
يجب ضبط مفتاح واجهة برمجة التطبيقات قبل بدء تشغيل "الخريطة".
يمكنك إجراء ذلك من خلال ضبط Map.apiKey
في معالِج حدث init()
لأي View
يحتوي على خريطة. يمكنك أيضًا ضبطه في GoogleMaps3DDemoApp.swift
قبل الاتصال بـ ContentView()
.
في GoogleMaps3DDemoApp.swift
، اضبط Map.apiKey
في معالِج الحدث onAppear
للعنصر WindowGroup
.
جلب مفتاح واجهة برمجة التطبيقات من ملف الإعدادات
استخدِم 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") {
}
}
}
}
في إغلاق الزر، أضِف رمزًا لتبديل حالة المتغيّر 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 جديدًا لعرض "العلامة" التجريبي.
أضِف ملف Swift جديدًا إلى مشروعك. يُرجى الاتصال برقم MarkerDemo.swift
.
أضِف مخطّط SwiftUI View وأدخِل الخريطة كما فعلت في 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 الخاص بهذا الدليل التعليمي.
إضافة ملف نموذج إلى مشروعك
أنشئ مجلدًا جديدًا في مشروع Xcode باسم Models
.
نزِّل النموذج من مستودع نماذج التطبيقات على GitHub. أضِفه إلى مشروعك من خلال سحبه إلى المجلد الجديد في عرض مشروع Xcode.
تأكَّد من ضبط الاستهداف ليكون الاستهداف الرئيسي لتطبيقك.
راجِع إعدادات "مراحل الإنشاء" > "نسخ موارد الحِزمة" لمشروعك. يجب أن يكون ملف النموذج في قائمة الموارد التي تم نسخها إلى الحِزمة. إذا لم يكن متوفّرًا، انقر على "+" لإضافته.
أضِف النموذج إلى تطبيقك.
أنشئ ملف 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) {
}
}
}
}
احصل على مسار النموذج من الحِزمة. أضِف الرمز الخاص بهذا الإجراء خارج 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
. في التطبيق الفعلي، قد يتم تحميل البيانات من ملف أو طلب بيانات من واجهة برمجة التطبيقات أو قاعدة بيانات.
أنشئ بعض عناصر الأشكال لإدارة بيانات الأشكال.
أضِف تعريفًا جديدًا لـ 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
لتوسيع المضلّعات إلى ارتفاع معيّن فوق سطح الأرض. - يتم استخدام
altitudeMode: .clampToGround
لجعل الخطوط المتعددة تتبع شكل سطح الأرض. - يتم ضبط الأنماط على عناصر
Polygon
من خلال ربط طلب إجراء بـstyleOptions()
بعد استدعاء.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
يمكنك من خلاله الحصول على رقم تعريف المكان لعلامة المكان التي تم النقر عليها.
يمكنك استخدام معرّف المكان للاطّلاع على مزيد من التفاصيل باستخدام حزمة تطوير البرامج (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 لتطبيقات النماذج.
أنشئ مجلدًا جديدًا في مشروع Xcode باسم JSON
.
اسحب flightpath.json
إلى مجلد JSON
في Xcode.
اضبط الاستهداف ليكون الاستهداف الرئيسي لتطبيقك. تأكَّد من أنّ إعدادات "موارد حِزم النسخ" في مشروعك تتضمّن هذا الملف.
أنشئ ملفين جديدَين بتنسيق Swift في تطبيقك باسم FlightPathData.swift
وFlightDataLoader.swift
.
انسخ الرمز البرمجي التالي إلى تطبيقك. ينشئ هذا الرمز البرمجي بنى وفئَات تقرأ ملفًا محليًا باسم "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
خريطة.
يتم إنشاء القائمة الفعلية للّقطات الرئيسية من خلال تكرار كل موقع في مسار الرحلة.
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 - كيفية إعداد خريطة ثلاثية الأبعاد باستخدام مفتاح واجهة برمجة التطبيقات وطريقة عرض تلقائية
- كيفية إضافة علامات وتصاميم ثلاثية الأبعاد وخطوط ومضلّعات إلى خريطتك
- كيفية التحكّم في الكاميرا لإنشاء صورة متحركة للانتقال إلى موقع آخر
- كيفية التعامل مع أحداث النقر على علامات الأماكن
ما هي الخطوات التالية؟
- اطّلِع على دليل المطوّر للحصول على مزيد من التفاصيل حول الإجراءات التي يمكنك اتّخاذها باستخدام حزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" الثلاثية الأبعاد لأجهزة iOS.
- يُرجى مساعدتنا في إنشاء المحتوى الذي قد يكون مفيدًا لك من خلال الإجابة عن الأسئلة التالية: