1. قبل البدء
يعلّمك هذا الدرس التطبيقي حول الترميز كيفية استخدام حزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" لأجهزة iOS مع SwiftUI.
المتطلبات الأساسية
- معرفة أساسية بلغة Swift
- معرفة أساسية بلغة SwiftUI
الإجراءات التي ستنفذّها
- فعِّل حزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" لتطبيقات iOS واستخدِمها لإضافة "خرائط Google" إلى تطبيق iOS باستخدام SwiftUI.
- أضِف علامات إلى الخريطة.
- تمرير الحالة بين عنصر SwiftUI وعنصر
GMSMapView
المتطلبات
- Xcode 11.0 أو إصدار أحدث
- حساب Google تم تفعيل الفوترة فيه
- حزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" لتطبيقات iOS
- قرطاج
2. طريقة الإعداد
في خطوة التفعيل التالية، فعِّل حزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" لتطبيقات iOS.
إعداد Google Maps Platform
إذا لم يكن لديك حساب على Google Cloud Platform ومشروع مفعَّل فيه نظام الفوترة، يُرجى الاطّلاع على دليل البدء باستخدام Google Maps Platform لإنشاء حساب فوترة ومشروع.
- في Cloud Console، انقر على القائمة المنسدلة الخاصة بالمشروع واختَر المشروع الذي تريد استخدامه في هذا الدرس العملي.
- فعِّل واجهات برمجة التطبيقات وحِزم تطوير البرامج (SDK) في Google Maps Platform المطلوبة لهذا الدرس العملي في Google Cloud Marketplace. لإجراء ذلك، اتّبِع الخطوات الواردة في هذا الفيديو أو هذه المستندات.
- أنشئ مفتاح واجهة برمجة التطبيقات في صفحة بيانات الاعتماد في Cloud Console. يمكنك اتّباع الخطوات الواردة في هذا الفيديو أو هذه المستندات. تتطلّب جميع الطلبات إلى "منصة خرائط Google" مفتاح واجهة برمجة تطبيقات.
3- تنزيل الرمز الأوّلي
لمساعدتك في البدء بأسرع ما يمكن، إليك بعض الرموز البرمجية الأولية لمساعدتك في متابعة هذا الدرس العملي. يمكنك الانتقال إلى الحلّ مباشرةً، ولكن إذا أردت اتّباع جميع الخطوات لإنشائه بنفسك، يمكنك مواصلة القراءة.
- استنسِخ المستودع إذا كان لديك
git
مثبَّتًا.
git clone https://github.com/googlecodelabs/maps-ios-swiftui.git
يمكنك بدلاً من ذلك النقر على الزر التالي لتنزيل رمز المصدر.
- بعد الحصول على الرمز، انتقِل في نافذة طرفية
cd
إلى الدليلstarter/GoogleMapsSwiftUI
. - نفِّذ الأمر
carthage update --platform iOS
لتنزيل حزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" لتطبيقات iOS - أخيرًا، افتح ملف
GoogleMapsSwiftUI.xcodeproj
في Xcode
4. نظرة عامة على الرمز البرمجي
في مشروع المبتدئين الذي نزّلته، تم توفير الفئات التالية وتنفيذها لك:
AppDelegate
:UIApplicationDelegate
التطبيق سيتم هنا إعداد حزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" لتطبيقات iOS.-
City
: بنية تمثّل مدينة (تحتوي على اسم المدينة وإحداثياتها). -
MapViewController
: مجموعة أدوات واجهة مستخدم محدودة النطاقUIViewController
تحتوي على "خريطة Google" (GMSMapView)SceneDelegate
:UIWindowSceneDelegate
الخاص بالتطبيق الذي يتم إنشاءContentView
منه.
بالإضافة إلى ذلك، تحتوي الفئات التالية على عمليات تنفيذ جزئية وستكملها بنفسك بحلول نهاية هذا الدرس التطبيقي:
-
ContentView
: هي طريقة العرض الرئيسية في SwiftUI التي تحتوي على تطبيقك. -
MapViewControllerBridge
: فئة تربط بين عرض UIKit وعرض SwiftUI. على وجه التحديد، هذا هو الصف الذي سيتيح الوصول إلىMapViewController
في SwiftUI.
5- مقارنة بين SwiftUI وUIKit
تم طرح SwiftUI في الإصدار 13 من نظام التشغيل iOS كإطار عمل بديل لواجهة المستخدم بدلاً من UIKit لتطوير تطبيقات iOS. تقدّم SwiftUI عددًا من المزايا مقارنةً بإطار عمل UIKit السابق. في ما يلي بعض الأمثلة:
- يتم تعديل طرق العرض تلقائيًا عند تغيُّر الحالة. باستخدام عناصر تُعرف باسم الحالة، سيؤدي أي تغيير في القيمة الأساسية التي تحتوي عليها إلى تعديل واجهة المستخدم تلقائيًا.
- تتيح المعاينات المباشرة إمكانية التطوير بشكل أسرع. تقلّل المعاينات المباشرة من الحاجة إلى إنشاء الرمز البرمجي ونشره في محاكي لرؤية التغييرات المرئية، إذ يمكن الاطّلاع على معاينة لعرض SwiftUI بسهولة على Xcode.
- يتم تخزين البيانات الأساسية في Swift. يتم تعريف جميع طرق العرض في SwiftUI باستخدام لغة Swift، لذا لم يعُد من الضروري استخدام Interface Builder.
- تعمل مع UIKit. تضمن إمكانية التشغيل التفاعلي مع UIKit إمكانية استخدام التطبيقات الحالية لـ SwiftUI بشكل تدريجي مع طرق العرض الحالية. بالإضافة إلى ذلك، يمكن استخدام المكتبات التي لا تتوافق مع SwiftUI بعد، مثل حزمة تطوير البرامج (SDK) لنظام التشغيل iOS في "خرائط Google"، في SwiftUI.
هناك بعض العيوب أيضًا:
- لا تتوفّر SwiftUI إلا على نظام التشغيل iOS 13 أو الإصدارات الأحدث.
- لا يمكن فحص بنية العرض في معاينات Xcode.
حالة SwiftUI وتدفّق البيانات
توفّر SwiftUI طريقة جديدة لإنشاء واجهة مستخدم باستخدام أسلوب تصريحي، إذ تخبر SwiftUI كيف تريد أن يظهر العرض مع جميع الحالات المختلفة له، وسيتولّى النظام الباقي. تتولّى SwiftUI تعديل العرض كلما تغيّرت الحالة الأساسية بسبب حدث أو إجراء من المستخدم. يُشار إلى هذا التصميم عادةً باسم تدفّق البيانات أحادي الاتجاه. على الرغم من أنّ تفاصيل هذا التصميم لا تندرج ضمن نطاق هذا الدرس التطبيقي حول الترميز، ننصحك بالاطّلاع على طريقة عمله في مستندات Apple حول الحالة وتدفّق البيانات.
ربط UIKit وSwiftUI باستخدام UIViewRepresentable أو UIViewControllerRepresentable
بما أنّ حزمة تطوير البرامج لنظام التشغيل iOS في "خرائط Google" تستند إلى UIKit ولا توفّر طريقة عرض متوافقة مع SwiftUI، يتطلّب استخدامها في SwiftUI الالتزام بأي من UIViewRepresentable
أو UIViewControllerRepresentable
. تتيح هذه البروتوكولات تضمين UIView
وUIViewController
تم إنشاؤهما باستخدام UIKit، على التوالي. على الرغم من إمكانية استخدام أي من البروتوكولين لإضافة خريطة Google إلى طريقة عرض SwiftUI، سنتناول في الخطوة التالية كيفية استخدام UIViewControllerRepresentable
لتضمين UIViewController
يحتوي على خريطة.
6. إضافة خريطة
في هذا القسم، ستضيف "خرائط Google" إلى طريقة عرض SwiftUI.
إضافة مفتاح واجهة برمجة التطبيقات
يجب تقديم مفتاح واجهة برمجة التطبيقات الذي أنشأته في خطوة سابقة إلى "حزمة تطوير البرامج لخرائط Google" المتوافقة مع iOS لربط حسابك بالخريطة التي سيتم عرضها على التطبيق.
لتوفير مفتاح واجهة برمجة التطبيقات، افتح الملف AppDelegate.swift
وانتقِل إلى الطريقة application(_, didFinishLaunchingWithOptions)
. يتم تهيئة حزمة SDK باستخدام GMSServices.provideAPIKey()
مع السلسلة "YOUR_API_KEY". استبدِل هذه السلسلة بمفتاح واجهة برمجة التطبيقات. سيؤدي إكمال هذه الخطوة إلى إعداد حزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" لنظام التشغيل iOS عند تشغيل التطبيق.
إضافة خريطة Google باستخدام MapViewControllerBridge
بعد توفير مفتاح واجهة برمجة التطبيقات لحزمة SDK، تتمثّل الخطوة التالية في عرض الخريطة على التطبيق.
يتضمّن عنصر التحكّم في العرض المقدَّم في رمز البداية، MapViewController
، عنصر GMSMapView
في طريقة عرضه. ومع ذلك، بما أنّه تم إنشاء وحدة التحكّم في العرض هذه في UIKit، عليك ربط هذه الفئة بـ SwiftUI حتى يمكن استخدامها داخل ContentView
. ولإجراء ذلك، يُرجى اتّباع الخطوات التالية:
- افتح الملف
MapViewControllerBridge
في Xcode.
يتوافق هذا الصف مع البروتوكول UIViewControllerRepresentable المطلوب لتضمين UIViewController
من UIKit حتى يمكن استخدامه كطريقة عرض في 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. عند تطبيقها على حالة، ستعرض ربطًا.
- يمكنك الآن تشغيل التطبيق لرؤية العلامات المعروضة على الخريطة.
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 جديد، عليك تعديل الدالة updateUIViewController_, context)
في MapViewControllerBridge
لكي يتم تحريك الخريطة إلى العلامة المحدّدة. يمكنك إجراء ذلك من خلال نسخ الرمز التالي:
struct MapViewControllerBridge: UIViewControllerRepresentable {
@Binding var selectedMarker: GMSMarker?
func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
markers.forEach { $0.map = uiViewController.map }
selectedMarker?.map = uiViewController.map
animateToSelectedMarker(viewController: uiViewController)
}
private func animateToSelectedMarker(viewController: MapViewController) {
guard let selectedMarker = selectedMarker else {
return
}
let map = viewController.map
if map.selectedMarker != selectedMarker {
map.selectedMarker = selectedMarker
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
map.animate(toZoom: kGMSMinZoomLevel)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
map.animate(with: GMSCameraUpdate.setTarget(selectedMarker.position))
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
map.animate(toZoom: 12)
})
}
}
}
}
}
ستنفّذ الدالة animateToSelectedMarker(viewController)
سلسلة من الصور المتحركة على الخريطة باستخدام الدالة animate(with)
في GMSMapView
.
- نقل
selectedMarker
من بطاقةContentView
إلىMapViewControllerBridge
بعد أن يتم الإعلان عن Binding الجديد في MapViewControllerBridge
، يمكنك تعديل 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
في هذه الخطوة، ستستمع إلى الأحداث المنبعثة من GMSMapView
، ثم سترسل هذا الحدث إلى SwiftUI. على وجه التحديد، عليك ضبط مفوّض لعرض الخريطة والاستماع إلى أحداث تحرّك الكاميرا، حتى أنّه عند التركيز على مدينة معيّنة وتحرّك كاميرا الخريطة من خلال إيماءة، سيتم إلغاء التركيز على عرض الخريطة لتتمكّن من رؤية المزيد من الخريطة.
Use SwiftUI Coordinators
GMSMapView
تُصدر أحداثًا، مثل تغييرات موضع الكاميرا أو عند النقر على علامة. آلية الاستماع إلى هذه الأحداث هي من خلال البروتوكول GMSMapViewDelegate. تقدّم SwiftUI مفهوم "المنسّق" الذي يُستخدَم تحديدًا للعمل كمفوّض لوحدات التحكّم في العرض في 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
كعنصر التحكّم في عرض الخريطة
بعد إنشاء أداة التنسيق المخصّصة، تتمثّل الخطوة التالية في ضبط أداة التنسيق كمفوّض لعرض الخريطة في وحدة التحكّم في العرض. لإجراء ذلك، عدِّل عملية تهيئة وحدة التحكّم في العرض في makeUIViewController(context)
. يمكن الوصول إلى أداة التنسيق التي تم إنشاؤها من الخطوة السابقة من عنصر 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 الخاص بك باستخدام حزمة تطوير البرامج لخدمة "خرائط Google" المتوافقة مع iOS.
ما تعلّمته
- الاختلافات بين SwiftUI وUIKit
- كيفية الربط بين SwiftUI وUIKit باستخدام UIViewControllerRepresentable
- كيفية إجراء تغييرات على طريقة عرض الخريطة باستخدام State وBinding
- كيفية إرسال حدث من عرض الخريطة إلى SwiftUI باستخدام Coordinator
ما هي الخطوات التالية؟
- حزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" لتطبيقات iOS
- المستندات الرسمية الخاصة بحزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" لتطبيقات iOS
- Places SDK for iOS: العثور على أنشطة تجارية محلية ونقاط اهتمام من حولك
- maps-sdk-for-ios-samples
- عيّنة للتعليمات البرمجية على GitHub توضّح جميع الميزات ضِمن حزمة تطوير البرامج بالاستناد إلى بيانات "خرائط Google" لأجهزة iOS
- SwiftUI: مستندات Apple الرسمية حول SwiftUI
- يُرجى مساعدتنا في إنشاء المحتوى الذي تراه الأكثر فائدة من خلال الإجابة عن الاستطلاع التالي:
ما هي جلسات الترميز الأخرى التي تريد المشاركة فيها؟
هل يتعذّر عليك العثور على الدرس العملي الذي يهمّك أكثر؟ يمكنك طلب ذلك من خلال تقديم مشكلة جديدة هنا.