1. قبل البدء
يشرح لك هذا الدرس التطبيقي كيفية استخدام SDK للخرائط لنظام التشغيل iOS مع SwiftUI.
المتطلّبات الأساسية
- المعرفة الأساسية لشركة Swift
- الإلمام الأساسي بـ SwiftUI
الإجراءات التي ستنفذّها
- فعِّل حزمة تطوير البرامج (SDK) لخدمة "خرائط Google" لنظام التشغيل iOS لإضافة "خرائط Google" إلى تطبيق iOS باستخدام SwiftUI.
- إضافة علامات إلى الخريطة.
- حالة الحالة من عرض SwiftUI إلى عنصر
GMSMapView
والعكس صحيح.
الأشياء التي تحتاج إليها
- Xcode 11.0 أو إصدار أحدث
- حساب Google تم تفعيل الفوترة به
- حزمة تطوير البرامج (SDK) لتطبيق "خرائط Google" لنظام التشغيل iOS
- القرطاج
2. الإعداد
بالنسبة إلى خطوة التفعيل التالية، فعِّل حزمة تطوير البرامج (SDK) لتطبيق "خرائط Google" لنظام التشغيل iOS.
إعداد "منصة خرائط Google"
إذا لم يكن لديك حساب على Google Cloud Platform ومشروع تم تفعيل الفوترة فيه، يُرجى الاطّلاع على دليل بدء استخدام "منصة خرائط Google" لإنشاء حساب فوترة ومشروع.
- في Cloud Console، انقر على القائمة المنسدلة للمشروع واختَر المشروع الذي تريد استخدامه لهذا الدرس التطبيقي.
- فعِّل واجهات برمجة تطبيقات ومنصة SDK لمنصة "خرائط Google" المطلوبة لهذا الدرس التطبيقي في 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" SDK لنظام التشغيل iOS - أخيرًا، افتح ملف
GoogleMapsSwiftUI.xcodeproj
في Xcode
4. نظرة عامة على الرمز
في مشروع المبتدئين الذي نزّلته، تم توفير الصفوف التالية وتنفيذها لك:
AppDelegate
- التطبيقUIApplicationDelegate
. سيتم إعداد حزمة تطوير البرامج (SDK) لخدمة "خرائط Google" لنظام التشغيل iOS.City
- مبنى يمثل مدينة (يحتوي على اسم المدينة وإحداثياتها).MapViewController
- واجهة مستخدمUIViewController
Kit بسيطة تحتوي على إحدى "خرائط Google" (GMSMapView)SceneDelegate
- التطبيقUIWindowSceneDelegate
الذي يتم إنشاء مثيلContentView
منه.
بالإضافة إلى ذلك، يكون للصفوف التالية عمليات تنفيذ جزئية وستُكملها بحلول نهاية هذا الدرس التطبيقي حول الترميز:
ContentView
- عرض SwiftUI ذو المستوى الأعلى الذي يتضمن تطبيقك.MapViewControllerBridge
- فئة تربط بين عرض UIKit وعرض SwiftUI. وعلى وجه التحديد، هذه هي الفئة التي ستجعل منMapViewController
الوصول إليها في SwiftUI.
5. استخدام SwiftUI مقابل UIKit
تم طرح SwiftUI في نظام التشغيل iOS 13 كإطار عمل بديل لواجهة المستخدم عبر UIKit لتطوير تطبيقات iOS. يوفّر SwiftUI ميزات إضافية مقارنةً بواجهة المستخدم السابقة UserKit. على سبيل المثال لا الحصر:
- يتم تعديل المشاهدات تلقائيًا عند تغيّر الحالة. باستخدام العناصر التي تُسمى الحالة، سيؤدي أي تغيير في القيمة الأساسية التي تحتوي عليها إلى تحديث واجهة المستخدم تلقائيًا.
- تساهم المعاينات المباشرة في تسريع عملية التطوير. تقلل المعاينات المباشرة الحاجة إلى إنشاء رمز ونشره إلى محاكي للاطّلاع على التغييرات المرئية إذ يمكن معاينة عرض SwiftUI بسهولة على Xcode.
- مصدر الحقيقة في Swift. تم الإعلان عن جميع الملفات الشخصية في SwiftUI في Swift، لذا لم يعُد استخدام "أداة إنشاء واجهات" ضروريًا.
- إمكانية التشغيل التفاعلي مع UIKit. وتضمن إمكانية التشغيل التفاعلي مع UIKit أن يكون بإمكان التطبيقات الحالية استخدام SwiftUI تدريجيًا مع طرق العرض الحالية. وبالإضافة إلى ذلك، يمكن استخدام SwiftUI في المكتبات التي لا تتوافق مع SwiftUI، مثل حزمة تطوير البرامج (SDK) لخدمة "خرائط Google" لنظام التشغيل iOS.
كما أن هناك بعض السلبيات:
- لا تتوفّر خدمة SwiftUI إلا على نظام التشغيل iOS 13 أو الإصدارات الأحدث.
- لا يمكن فحص العرض الهرمي في معاينات Xcode.
حالة SwiftUI وتدفق البيانات
توفّر SwiftUI طريقة مبتكرة لإنشاء واجهة مستخدم باستخدام نهج تعريفي. يمكنك إخبار SwiftUI بالطريقة التي تريد أن يظهر بها العرض الخاص بك مع جميع الحالات المختلفة له، وسيهتم النظام بالباقي. تتعامل SwiftUI مع تحديث العرض كلما تغيرت الحالة الأساسية بسبب حدث أو إجراء المستخدم. يُشار إلى هذا التصميم عادةً باسم تدفق البيانات الأحادية الاتجاه. على الرغم من أن تفاصيل هذا التصميم لا تقع ضمن نطاق هذا الدرس التطبيقي، ننصحك بالاطّلاع على كيفية عمل هذا في مستندات Apple بشأن التدفق والبيانات.
استخدام معرّف UIKit وSwiftUI باستخدام UIViewRepresentable أو UIViewControllerRepresentable
وبما أنّ حزمة تطوير البرامج (SDK) لخدمة "خرائط Google" لنظام التشغيل iOS مُصمَّمة استنادًا إلى UIKit، ولا توفِّر بعد عرضًا متوافقًا مع SwiftUI، يتطلّب استخدامها في SwiftUI التوافق مع UIViewRepresentable
أو UIViewControllerRepresentable
. تتيح هذه البروتوكولات SwiftUI تضمين الترميزَين UIView
وUIViewController
المبنيَين في UIKit على التوالي. يمكنك استخدام أي من البروتوكولين لإضافة خريطة Google إلى طريقة عرض SwiftUI، ولكن في الخطوة التالية، سنلقي نظرة على استخدام UIViewControllerRepresentable
لتضمين UIViewController
يحتوي على خريطة.
6- إضافة خريطة
في هذا القسم، ستضيف خرائط Google إلى طريقة عرض SwiftUI.
إضافة مفتاح واجهة برمجة التطبيقات
يجب تقديم مفتاح واجهة برمجة التطبيقات الذي أنشأته في خطوة سابقة إلى SDK للخرائط لنظام التشغيل iOS لربط حسابك بالخريطة التي سيتم عرضها على التطبيق.
لتقديم مفتاح واجهة برمجة التطبيقات، افتح ملف AppDelegate.swift
وانتقِل إلى طريقة application(_, didFinishLaunchingWithOptions)
. في الوقت الحالي، يتم إعداد حزمة تطوير البرامج (SDK) من خلال GMSServices.provideAPIKey()
بالسلسلة "YOUR_API_KEY". استبدِل هذه السلسلة بمفتاح واجهة برمجة التطبيقات. سيؤدي إكمال هذه الخطوة إلى إعداد حزمة تطوير البرامج (SDK) لتطبيق "خرائط Google" لنظام التشغيل iOS عند بدء تشغيل التطبيق.
إضافة خريطة Google باستخدام MapViewControllerB تجريد
الآن وبعد تقديم مفتاح واجهة برمجة التطبيقات إلى حزمة تطوير البرامج (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
الآن على النحو التالي:
وحدة تحكم MapViewController
import GoogleMaps
import SwiftUI
struct MapViewControllerBridge: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> MapViewController {
return MapViewController()
}
func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
}
}
استخدام MapViewControllerB Bridge في ContentView
الآن وبعد أن أنشأ MapViewControllerBridge
مثيل MapViewController
، تتمثّل الخطوة التالية في استخدام هذه البنية ضمن ContentView
لعرض خريطة.
- افتح الملف
ContentView
في Xcode.
يتم إنشاء مثيل ContentView
في SceneDelegate
ويحتوي على عرض التطبيق ذي المستوى الأعلى. ستتم إضافة الخريطة من داخل هذا الملف.
- ويجب إنشاء
MapViewControllerBridge
ضمن السمةbody
.
ضمن السمة body
لهذا الملف، تم مسبقًا توفير ZStack
وتنفيذها. يحتوي ZStack
حاليًا على قائمة قابلة للتفاعل والسحب للمدن التي ستستخدمها في خطوة لاحقة. في الوقت الحالي، ضمن ZStack
، يمكنك إنشاء MapViewControllerBridge
كأول عرض فرعي لـ ZStack
بحيث يتم عرض الخريطة في التطبيق خلف قائمة المدن. عند إجراء ذلك، يجب أن يظهر محتوى السمة body
ضمن 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
. تجدر الإشارة إلى أن هذا الموقع يتضمّن تعليقات توضيحية باستخدام برنامج تضمين خاصية SwiftUI State للإشارة إلى أنه يجب أن يديره SwiftUI. وبالتالي، في حال اكتشاف أي تغييرات في هذا الموقع، مثل إضافة علامة أو إزالتها، سيتم تعديل الملفات الشخصية التي تستخدم هذه الحالة.
مشاهدة المحتوى
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
.
قائمة المدن
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)
}
}
}
}
تمرير الحالة إلى MapViewControllerBbridge عبر الربط
بالإضافة إلى قائمة المدن التي تعرض البيانات من السمة markers
، مرِّر هذه الخاصية إلى البنية MapViewControllerBridge
بحيث يمكن استخدامها لعرض تلك العلامات على الخريطة. اتّبِع الخطوات التالية لإجراء ذلك:
- الإعلان عن خاصية
markers
جديدة فيMapViewControllerBridge
وإضافة تعليقات توضيحية عليها@Binding
وحدة تحكم MapViewController
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
خاصية ولاية تُسمى selectedMarker
ويتم ضبطها على "القيمة" ويتم تعديلها عندما يتم اختيار مدينة في القائمة. تتم معالجة هذه المشكلة من خلال الملف الشخصي CitiesList
في buttonAction
ضمن ContentView
.
مشاهدة المحتوى
CitiesList(markers: $markers) { (marker) in
guard self.selectedMarker != marker else { return }
self.selectedMarker = marker
// ...
}
عندما يتم تغيير selectedMarker
، يجب أن يكون MapViewControllerBridge
على علم بهذا التغيير في الحالة حتى يتمكّن من تحريك الخريطة إلى محدّد الموقع المحدّد. وبالتالي، عليك تحديد عملية ربط جديدة ضمن السمة MapViewControllerBridge
من النوع GMSMarker
، ثم تسمية الخاصية selectedMarker
.
وحدة تحكم MapViewController
struct MapViewControllerBridge: UIViewControllerRepresentable {
@Binding var selectedMarker: GMSMarker?
}
- تحديث
MapViewControllerBridge
لتحريك الخريطة عند تغييرselectedMarker
عندما يتم الإعلان عن ربط جديد، يلزمك تحديث دالة MapViewControllerBridge
's updateUIViewController_, context)
بحيث تتحرك الخريطة إلى العلامة المحددة. يمكنك إجراء ذلك من خلال نسخ الرمز التالي:
struct MapViewControllerBridge: UIViewControllerRepresentable {
@Binding var selectedMarker: GMSMarker?
func updateUIViewController(_ uiViewController: MapViewController, context: Context) {
markers.forEach { $0.map = uiViewController.map }
selectedMarker?.map = uiViewController.map
animateToSelectedMarker(viewController: uiViewController)
}
private func animateToSelectedMarker(viewController: MapViewController) {
guard let selectedMarker = selectedMarker else {
return
}
let map = viewController.map
if map.selectedMarker != selectedMarker {
map.selectedMarker = selectedMarker
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
map.animate(toZoom: kGMSMinZoomLevel)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
map.animate(with: GMSCameraUpdate.setTarget(selectedMarker.position))
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
map.animate(toZoom: 12)
})
}
}
}
}
}
ستعمل الدالة animateToSelectedMarker(viewController)
على إنشاء سلسلة من الرسوم المتحركة للخريطة باستخدام الدالة GMSMapView
's animate(with)
.
- تخطّي
ContentView
منselectedMarker
إلىMapViewControllerBridge
بعد الإعلان عن الربط الجديد لـ MapViewControllerBridge
، يُرجى تعديل ContentView
لاجتياز selectedMarker
حيث يتم إنشاء مثيل MapViewControllerBridge
.
مشاهدة المحتوى
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)
.
وحدة تحكم MapViewController
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
.
مشاهدة المحتوى
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. على وجه التحديد، سيتم تعيين مفوّض لعرض الخريطة والاستماع إلى أحداث تحريك الكاميرا بحيث عندما يتم التركيز على المدينة ويتحرك كاميرا الخريطة من إيماءة، سيتم إلغاء تركيز عرض الخريطة حتى تتمكن من مشاهدة المزيد من الخريطة.
استخدام منسِّقي SwiftUI
ينبعث GMSMapView
من الأحداث مثل تغييرات موضع الكاميرا أو عند النقر على محدّد موقع. آلية الاستماع إلى هذه الأحداث من خلال بروتوكول GMSMapViewDelegate. تقدِّم SwiftUI مفهوم المنسِّق، الذي يُستخدَم على وجه التحديد كمفوَّض لوحدات التحكُّم في عرض UIKit. لذا، في عالم SwiftUI، يجب أن يكون المنسِّق مسؤولاً عن التوافق مع بروتوكول GMSMapViewDelegate
. ولإجراء ذلك، أكمِل الخطوات التالية:
- إنشاء منسّق باسم
MapViewCoordinator
ضمنMapViewControllerBridge
إنشاء صف دراسي مدمج داخل الصف MapViewControllerBridge
وسيطلق عليه MapViewCoordinator
. يجب أن تتوافق هذه الفئة مع GMSMapViewDelegate
ويجب أن تعلن عن MapViewControllerBridge
كسمة.
وحدة تحكم MapViewController
struct MapViewControllerBridge: UIViewControllerRepresentable {
// ...
final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
var mapViewControllerBridge: MapViewControllerBridge
init(_ mapViewControllerBridge: MapViewControllerBridge) {
self.mapViewControllerBridge = mapViewControllerBridge
}
}
}
- تنفيذ
makeCoordinator()
فيMapViewControllerBridge
بعد ذلك، نفِّذ الطريقة makeCoordinator()
ضمن MapViewControllerBridge
واعرض مثيل MapViewCoodinator
الذي أنشأته في الخطوة السابقة.
وحدة تحكم MapViewController
struct MapViewControllerBridge: UIViewControllerRepresentable {
// ...
func makeCoordinator() -> MapViewCoordinator {
return MapViewCoordinator(self)
}
}
- تحديد
MapViewCoordinator
كمفوّض الخريطة
مع إنشاء المُنظّم المخصص، تتمثّل الخطوة التالية في تعيين المفوّض كمفوّض لعرض وحدة التحكم على الخريطة. ولإجراء ذلك، يمكنك تعديل إعداد وحدة التحكُّم في العرض في makeUIViewController(context)
. يمكن الوصول إلى المنسق الذي تم إنشاؤه من الخطوة السابقة من كائن "السياق".
وحدة تحكم MapViewController
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 من التفاعل فقط مع أحداث تحريك الكاميرا المتعلقة بالإيماءات.
وحدة تحكم MapViewController
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
إذا كان حدث النقل مرتبطًا بإيماءة. سيؤدي ذلك إلى إظهار الخريطة بشكل كامل مرة أخرى عندما يتم تحريك الخريطة بإيماءة.
مشاهدة المحتوى
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 الخاص بك باستخدام حزمة تطوير البرامج (SDK) لخدمة "خرائط Google" لنظام التشغيل iOS.
ما تعلّمته
- الاختلافات بين SwiftUI وUIKit
- كيفية الربط بين SwiftUI وUIKit باستخدام UIViewControllerRepresentable
- كيفية إجراء تغييرات على وضع الخريطة باستخدام الحالة والربط
- كيفية إرسال حدث من وضع الخريطة إلى SwiftUI باستخدام Coordinator
ما الخطوات التالية؟
- حزمة تطوير البرامج (SDK) لتطبيق "خرائط Google" لنظام التشغيل iOS - المستندات الرسمية لحزمة تطوير البرامج (SDK) لتطبيق "خرائط Google" لنظام التشغيل iOS
- أماكن SDK لنظام التشغيل iOS - ابحث عن الأنشطة التجارية المحلية ونقاط الاهتمام من حولك
- maps-sdk-for-ios-samples - رمز نموذجي في GitHub يوضّح جميع الميزات ضمن "SDK للخرائط" لنظام التشغيل iOS.
- SwiftUI - مستندات رسمية من Apple's على SwiftUI
- يُرجى مساعدتنا في إنشاء المحتوى الذي قد تجده أكثر فائدة من خلال الإجابة عن السؤال التالي:
ما هي الدروس التطبيقية الأخرى حول الترميز التي تريد الاطّلاع عليها؟
هل الدرس التطبيقي حول الترميز الذي تريده غير مدرج أعلاه؟ طلب حلول للمشكلة الجديدة هنا