
लेआउट को पसंद के मुताबिक बनाने की सुविधा का इस्तेमाल करके, व्यू हैरारकी (व्यू और व्यू ग्रुप के लेआउट का क्रम) और स्क्रीन प्लेसमेंट को कंट्रोल करें. इससे Google के उपलब्ध कराए गए व्यू और आपके कारोबार के कस्टम व्यू, दोनों को कंट्रोल किया जा सकता है.
पहले से तय किए गए लेआउट स्लॉट में कस्टम व्यू जोड़ने के बजाय, पूरी स्क्रीन के लेआउट को मैनेज करने के लिए, लेआउट डेलिगेट लागू करें. नेविगेशन के दौरान स्टेट ट्रांज़िशन, आपके लेआउट डेलिगेट को Google से मिले कॉम्पोनेंट मिलते हैं. जैसे, टर्न कार्ड, ईटीए कार्ड, और बटन. स्टैंडर्ड iOS ऑटो लेआउट कंस्ट्रेंट का इस्तेमाल करके, इन एलिमेंट को अपने कस्टम कॉन्टेंट के साथ रखें.
इस फ़्रेमवर्क का इस्तेमाल करके, कारोबार की कस्टम जानकारी को स्क्रीन पर सटीक तरीके से दिखाया जा सकता है. जैसे, ऑर्डर का स्टेटस या पिकअप के निर्देश. इससे आपके व्यू ओवरलैप होने से बचते हैं.
लेआउट को पसंद के मुताबिक बनाने की सुविधा कैसे काम करती है
लेआउट को पसंद के मुताबिक बनाने के लिए, डेलिगेशन डिज़ाइन पैटर्न का इस्तेमाल किया जाता है. स्क्रीन पर यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट को अपने-आप ड्रॉ या पोज़िशन करने के बजाय, SDK टूल उन्हें सीधे आपकी लिखी गई कस्टम क्लास में पास करता है: लेआउट डेलिगेट.
अपने लेआउट को पसंद के मुताबिक बनाने के लिए,NavigationLayoutDelegate प्रोटोकॉल के मुताबिक एक क्लास बनाएं और अपने GMSMapView के setLayoutDelegate(_:) तरीके में एक इंस्टेंस पास करें. जब भी नेविगेशन की स्थिति बदलती है, जैसे कि बुनियादी मैप से मोड़-दर-मोड़ दिशा-निर्देशों पर जाना, तब SDK टूल आपके डेलिगेट पर कॉलबैक के तरीके लागू करता है. साथ ही, उस स्थिति के लिए उपलब्ध यूज़र इंटरफ़ेस (यूआई) कॉम्पोनेंट उपलब्ध कराता है.
उपयोगकर्ताओं को बेहतर और इंटिग्रेट किया गया अनुभव देने के लिए, यह फ़्रेमवर्क आपके ऐप्लिकेशन और एसडीके के बीच ज़िम्मेदारी को साफ़ तौर पर बांटता है. इनमें बदलाव करने के लिए, लेआउट डेलिगेट का इस्तेमाल करें:
- व्यू हैरारकी (व्यू और व्यू ग्रुप के लेआउट का क्रम) बनाएं: हर यूज़र इंटरफ़ेस (यूआई) स्टेट के लिए, स्क्रीन पर जोड़े जाने वाले Google कॉम्पोनेंट और कस्टम बिज़नेस व्यू चुनें.
- हर एलिमेंट की पोज़िशन सेट करें: स्क्रीन ऐंकर, मार्जिन, और लेआउट प्लेसमेंट को सटीक तरीके से सेट करें. Google कॉम्पोनेंट पर कस्टम चौड़ाई या ऊंचाई की पाबंदियां लागू करने से बचें. ऐसा इसलिए, क्योंकि Google कॉम्पोनेंट अपने इंटरनल डाइमेंशन का हिसाब खुद लगाते हैं.
- बुनियादी मैप को फ़्रेम करना: व्यूपोर्ट कॉम्पोनेंट के निर्देशांकों का इस्तेमाल करके, मैप कैमरे की दिखने वाली सीमाओं को तय करें.
- स्क्रीन पर लेयर बनाएं: तय करें कि कस्टम व्यू, Google के पहले से मौजूद कंट्रोल के ऊपर, नीचे या बगल में दिखें.
इस दौरान, लेआउट डेलिगेट की मदद से Google के इन कॉम्पोनेंट को पसंद के मुताबिक नहीं बनाया जा सकता:
- कॉम्पोनेंट डाइमेंशन: Google के कॉम्पोनेंट का साइज़ और इंटरनल डाइमेंशन, जिनका हिसाब एसडीके अपने-आप लगाता है.
- ट्रिगर होने की शर्तें: रीयल-टाइम रूट डेटा के आधार पर, डाइनैमिक सूचनाएं या प्रॉम्प्ट कब दिखते हैं.
लागू करने के सिद्धांत
लेआउट डेलिगेट लिखते समय, लेआउट की गड़बड़ियों या रनटाइम क्रैश से बचने के लिए, इन नियमों का ध्यान रखें:
- डिफ़ॉल्ट रूप से खाली स्क्रीन दिखती है: Google के कॉम्पोनेंट सिर्फ़ तब दिखते हैं, जब आपका डेलिगेट उन्हें व्यू हैरारकी (व्यू और व्यू ग्रुप के लेआउट का क्रम) में जोड़ता है और उनकी पोज़िशन तय करता है.
- लेगसी एपीआई काम नहीं करते: लेगसी स्लॉट-आधारित लेआउट मॉडल को कंट्रोल करने वाली प्रॉपर्टी और तरीके काम नहीं करेंगे. साथ ही, हो सकता है कि कस्टम डेलिगेट के चालू होने पर, वे उम्मीद के मुताबिक काम न करें.
- इंटरनल व्यू स्ट्रक्चर में बदलाव न करें: Google के कॉम्पोनेंट (जैसे, टर्न कार्ड या ईटीए कार्ड) की व्यू हैरारकी (व्यू और व्यू ग्रुप के लेआउट का क्रम) में बदलाव न करें. ये इंटरनल व्यू हैरारकी, लागू करने से जुड़ी जानकारी होती हैं. इसलिए, एसडीके के अलग-अलग रिलीज़ में इनमें बदलाव हो सकता है. इनमें बदलाव करने से, आने वाले समय में SDK टूल के अपडेट के दौरान आपका लेआउट टूट सकता है.
लेगसी एपीआई के साथ काम करता है या नहीं
कस्टम लेआउट डेलिगेट का इस्तेमाल करते समय, लेआउट के भरोसेमंद व्यवहार के लिए, स्लॉट पर आधारित इन लेगसी एपीआई का इस्तेमाल करने से बचें. इन्हें बंद करने की योजना बनाई गई है. किसी मौजूदा ऐप्लिकेशन को माइग्रेट करने के लिए, इन एपीआई के इस्तेमाल को अपने कस्टम लेआउट डेलिगेट में कोड से बदलें:
स्लॉट पर आधारित लेगसी एपीआई दिखाएं
| लेगसी एपीआई | Layout Delegate को बदलना |
|---|---|
isNavigationHeaderEnabled |
startGuidance(navMap:newState:oldState:) में turnCard व्यू पढ़ें. |
isNavigationFooterEnabled |
startGuidance(navMap:newState:oldState:) में etaCard व्यू पढ़ें. |
isNavigationTripProgressBarEnabled |
startGuidance(navMap:newState:oldState:) में tripProgressBar व्यू पढ़ें. |
isNavigationReportIncidentButtonEnabled |
buttons कलेक्शन में .reporting बटन ढूंढें. |
setHeaderAccessoryView(_:) और
invalidateLayout(forAccessoryView:) |
लेआउट में अपना व्यू जोड़ें और उसे turnCard पर ऐंकर करें. |
padding और paddingAdjustmentBehavior |
कैमरे को फ़्रेम करने के लिए, viewport व्यू को अडजस्ट करें. |
navigationHeaderLayoutGuide |
अपने कस्टम व्यू को सीधे turnCard पर ऐंकर करें. |
navigationFooterLayoutGuide |
अपने कस्टम व्यू को सीधे etaCard पर ऐंकर करें. |
bottomTrailingButtonsLayoutGuide |
बटन को सीधे etaCard या कंटेनर गाइड पर ऐंकर करें. |
bottomTrailingButtonsAlternateLayoutGuide |
अपने बटन को सीधे .reporting बटन या wideMode कंस्ट्रेंट से ऐंकर करें. |
navigatorWillPresentPrompt(_:) और
navigatorDidDismissPrompt(_:) |
NavigationLayoutDelegate प्रॉम्प्ट कॉलबैक पर भरोसा करें, जैसे कि showPrompt(navMap:newPrompt:). |
कस्टम लेआउट डेलिगेट सेट होने पर, लेगसी लेआउट गाइड (जैसे कि navigationHeaderLayoutGuide या bottomTrailingButtonsLayoutGuide) और हेडर ऐक्सेसरी व्यू के तरीकों का कोई असर नहीं होता. इसके बजाय, Google के व्यू के हिसाब से अपने व्यू तय करें.
नेविगेशन यूज़र इंटरफ़ेस (यूआई) बंद होने पर भी, GMSUISettings की compassButton प्रॉपर्टी से कंपास को कंट्रोल किया जा सकता है. नेविगेशन चालू होने पर, आपका डेलिगेट यह तय करता है कि buttons कलेक्शन से कंपास को दिखाना है या नहीं.
ज़रूरी चीज़ों की चेकलिस्ट
अपने लेआउट डेलिगेट को लागू करने के लिए, इन ज़रूरी चरणों और शर्तों को पूरा करें:
-
नेविगेशन चालू करने से पहले, डेलिगेट सेट करें:
isNavigationEnabledयाenableNavigation(with:)की मदद से नेविगेशन चालू करने से पहले, अपनेGMSMapViewपरsetLayoutDelegate(_:)को कॉल करें. अगर नेविगेशन पहले से चालू है या आपने CarPlay स्क्रीन के लिए मैप व्यू बनाया है, तो इस तरीके से गड़बड़ी का मैसेज दिखता है. मैप व्यू, आपके डेलिगेट का रेफ़रंस सेव रखता है. पहले से मौजूद लेआउट को वापस लाने के लिए, नेविगेशन बंद होने परnilपास करें.// Set the layout delegate before you enable navigation. do { try mapView.setLayoutDelegate(MyLayoutDelegate()) } catch { print("Couldn't set the layout delegate: \(error)") } mapView.isNavigationEnabled = true
-
ज़रूरी व्यू अटैच करें: आपको Google का लोगो (
googleLogo) और व्यूपोर्ट (viewport) को सभी नेविगेशन स्टेटस में अपने लेआउट कंटेनर में जोड़ना होगा. -
तय किए गए डाइमेंशन का पालन करें: एसडीके, Google के उपलब्ध कराए गए कई कॉम्पोनेंट के डाइमेंशन तय करता है. इन एलिमेंट पर, कस्टम चौड़ाई या ऊंचाई की पाबंदियां लागू न करें:
- रास्ते की जानकारी वाला कार्ड
- ईटीए कार्ड
- आने वाले प्रॉम्प्ट
- Google का लोगो
- स्पीड विजेट
-
एलिमेंट को सबव्यू के तौर पर जोड़ना: Google के उपलब्ध कराए गए व्यू को सीधे अपने कस्टम कंटेनर व्यू या
GMSMapViewमें सबव्यू के तौर पर जोड़ें. -
लेआउट गाइड का इस्तेमाल करके अलाइन करना: Google के उपलब्ध कराए गए व्यू में, पहले से मौजूद विज़ुअल मार्जिन शामिल होते हैं. Google के कॉम्पोनेंट के साथ अपने कस्टम एलिमेंट को सही तरीके से अलाइन करने के लिए, अपने व्यू को Google व्यू की
layoutMarginsGuideप्रॉपर्टी तक सीमित करें. -
स्टेट एग्ज़िट पर क्लीन अप करना:
stopGuidance(navMap:newState:oldState:)में, नेविगेशन के लिए तैयार लेआउट को वापस लाने से पहले, चालू गाइडेंस व्यू हटाएं और उनकी पाबंदियों को बंद करें.disableNavigationUI(navMap:oldState:)में, जोड़े गए सभी व्यू और लेआउट गाइड हटाएं. इनमें स्क्रीन पर मौजूद कोई भी प्रॉम्प्ट शामिल है. इससे, एक व्यू दूसरे व्यू को ओवरलैप नहीं करता. -
सुलभता और दाएं से बाएं लिखे जाने वाले टेक्स्ट के लिए सहायता: Google के उपलब्ध कराए गए व्यू, सुलभता मानकों के मुताबिक होते हैं. हालांकि, पूरे लेआउट की सुलभता की ज़िम्मेदारी आपके डेलिगेट की होती है. दाईं से बाईं ओर लिखी जाने वाली भाषाओं के लिए, लेफ़्ट और राइट ऐंकर के बजाय, लीडिंग और ट्रेलिंग ऐंकर का इस्तेमाल करें.
-
लेगसी लेआउट एपीआई का इस्तेमाल करने से बचें: कस्टम डेलिगेट के चालू होने पर,
GMSUISettingsपर लेआउट में बदलाव करने वाली लेगसी प्रॉपर्टी याGMSMapViewपर लेआउट की लेगसी गाइड का इस्तेमाल न करें. कस्टम डेलिगेट अटैच होने पर, हो सकता है कि एसडीके इन लेगसी कॉल को अनदेखा कर दे.
यूज़र इंटरफ़ेस (यूआई) की स्थितियां और Google के कॉम्पोनेंट
जब नेविगेशन की स्थिति में बदलाव होता है, तो SDK टूल आपके डेलिगेट कॉलबैक को रीड-ओनली NavigationUIState ऑब्जेक्ट पास करता है. यह ऑब्जेक्ट, मौजूदा लेआउट कॉन्फ़िगरेशन फ़्लैग को Google कॉम्पोनेंट (उदाहरण के लिए, UIView इंस्टेंस) के साथ बंडल करता है. इनकी ज़रूरत आपको स्क्रीन रेंडर करने के लिए होती है.
लेआउट डेलिगेट, चार ऑपरेशनल स्टेट में व्यू हैरारकी (व्यू और व्यू ग्रुप के लेआउट का क्रम) को मैनेज करता है. यहां दिए गए डायग्राम में दिखाया गया है कि एसडीके, नेविगेशन की स्थितियों के बीच कैसे ट्रांज़िशन करता है. साथ ही, यह भी दिखाया गया है कि यह आपके डेलिगेट पर कौनसे कॉलबैक तरीके लागू करता है:
आपके डेलिगेट को दिए गए Google के कॉम्पोनेंट, नेविगेशन लाइफ़साइकल के मौजूदा फ़ेज़ पर निर्भर करते हैं.
ज़रूरी कॉम्पोनेंट (सभी नेविगेशन स्टेट)
नेविगेशन के मौजूदा फ़ेज़ के बावजूद, आपको अपनी व्यू हैरारकी (व्यू और व्यू ग्रुप के लेआउट का क्रम) में, Google के इन कॉम्पोनेंट को शामिल करना होगा, उनकी पोज़िशन तय करनी होगी, और उन्हें दिखाना होगा:
Google का लोगो (
googleLogo): यह कॉम्पोनेंट, Google Maps का लोगो दिखाता है. यह लोगो दिखाना ज़रूरी है. अगर फिर से बीच में लाएं बटन चालू है, तो ड्राइवर के मैप को स्क्रोल करके वाहन से दूर ले जाने पर, लोगो अपने-आप इस बटन में बदल जाता है. इसलिए, लोगो की जगह से तय होता है कि फिर से बीच में लाएं बटन कहां दिखेगा. हमारा सुझाव है कि लोगो को अपने लेआउट के निचले-बाएं कोने में रखें, ताकि वह स्टैंडर्ड मैप के यूज़र इंटरफ़ेस (यूआई) के मुताबिक दिखे.व्यूपोर्ट (
viewport): यह एक ऐसाUIViewहोता है जो दिखता नहीं है. इससे कैमरे की फ़्रेमिंग की सीमाएं तय होती हैं. व्यूपोर्ट की जगह से यह तय होता है कि SDK टूल, वाहन के शेवरॉन को कहां सेंटर करता है और चालू रास्ते की लाइन कहां बनाता है. व्यूपोर्ट को इस तरह से सेट करें कि वह आपकी स्क्रीन के खुले और साफ़ तौर पर दिखने वाले हिस्से को कवर करे. साथ ही, कस्टम बॉटम शीट जैसे अपारदर्शी ओवरले से सुरक्षित रहे.
नेविगेशन के लिए तैयार कॉम्पोनेंट
नेविगेशन के लिए तैयार होने के दौरान, यूज़र इंटरफ़ेस (यूआई) को कम से कम रखा जाता है, ताकि बुनियादी मैप पर फ़ोकस बना रहे. NavigationReadyUIState ऑब्जेक्ट से, इन चीज़ों को ऐक्सेस किया जा सकता है:
- बटन: बुनियादी मैप के लिए कॉन्फ़िगर किए गए फ़्लोटिंग ऐक्शन व्यू का कलेक्शन (आम तौर पर, सिर्फ़ कंपास बटन). Google, SDK टूल की आने वाली रिलीज़ में बटन जोड़ सकता है या उनके क्रम में बदलाव कर सकता है. इसलिए, अरे इंडेक्स की तय वैल्यू पर भरोसा न करें. इसके बजाय, ऐरे को इटरेट करें और हर बटन के टाइप को पढ़ें. इसके लिए, उसकी
typeप्रॉपर्टी की तुलनाNavigationButtonKnownType.compassसे करें. साथ ही, उसकीviewप्रॉपर्टी को कॉल करके फ़िज़िकल व्यू को निकालें. ध्यान दें कि इस कलेक्शन में मौजूद बटन, ऐक्टिव गाइडेंस के दौरान उपलब्ध बटन से अलग होते हैं.
चालू गाइडेंस कॉम्पोनेंट
मोड़-दर-मोड़ दिशा-निर्देश मिलने की सुविधा शुरू होने पर, SDK टूल नेविगेशन कंट्रोल के पूरे सुइट को अनलॉक कर देता है. आपका ActiveGuidanceUIState ऑब्जेक्ट, Google के इन कॉम्पोनेंट का ऐक्सेस देता है:
turnCard: प्राइमरी हेडर बैनर, जिसमें आगे की दिशाओं, दूरी के माप, और लेन के दिशा-निर्देशों की जानकारी दिखती है. इसे अपने लेआउट में सबसे ऊपर रखें, ताकि नेविगेशन की हैरारकी को आसानी से समझा जा सके. साथ ही, इसके आस-पास अपने कस्टम व्यू को ऐंकर करें.etaCard: फ़ुटर बैनर, जिसमें मंज़िल पर पहुंचने का अनुमानित समय, यात्रा का बचा हुआ समय, और मंज़िल तक पहुंचने की बची हुई दूरी दिखती है. इसे अपनी स्क्रीन के सबसे नीचे वाले किनारे पर रखें या इसके कोऑर्डिनेट को अपनी पसंद के मुताबिक बनाई गई टास्क-मैनेजमेंट शीट के साथ इंटिग्रेट करें.tripProgressBar: यह वर्टिकल प्रोग्रेस बार है. इससे ड्राइवर को पता चलता है कि उसने मौजूदा रास्ते पर कितनी दूरी तय कर ली है. लेगसी लेआउट में, इसे मैप के किनारे पर ही सेट किया जा सकता था. हालांकि, अब इसे कहीं भी सेट किया जा सकता है. उदाहरण के लिए, कस्टम कंटेनर के किसी किनारे पर.speedWidget: स्पीडोमीटर और स्पीड लिमिट के लिए फ़्लोटिंग कंट्रोल. आपकी एपीआई सेटिंग (GMSMapViewकीshouldDisplaySpeedometerऔरshouldDisplaySpeedLimitप्रॉपर्टी) और डेटा की उपलब्धता के आधार पर, यह व्यू रनटाइम के दौरान चार विज़ुअल स्टेट के बीच डाइनैमिक तौर पर साइज़ बदलता है. ये स्टेट हैं: कुछ भी न दिखाना, सिर्फ़ मौजूदा स्पीड दिखाना, सिर्फ़ स्पीड लिमिट दिखाना या दोनों रीडिंग दिखाना. विजेट बिना किसी सूचना के इन साइज़ के बीच शिफ़्ट हो सकता है. इसलिए, आस-पास के व्यू को हमेशा कंस्ट्रेंट का इस्तेमाल करके ऐंकर करें, ताकि आपका लेआउट अपने-आप अडजस्ट हो जाए और स्पेस ओवरलैप न हो.
buttons: ऐक्टिव गाइडेंस स्टेट के लिए, फ़्लोटिंग ऐक्शन व्यू का बड़ा किया गया कलेक्शन. आम तौर पर, इसमें कंपास और ट्रैफ़िक की जानकारी देने वाला बटन, दोनों शामिल होते हैं. नेविगेशन के लिए तैयार स्थिति की तरह ही, अरे कोtype(.compassया.reporting) के हिसाब से फ़िल्टर करके, अलग-अलग बटन व्यू का पता लगाया जा सकता है और उन्हें निकाला जा सकता है. साथ ही,viewप्रॉपर्टी का इस्तेमाल करके, फ़िज़िकल व्यू को निकाला जा सकता है. इसके बाद, उन्हें अलग-अलग पोज़िशन किया जा सकता है याGMSNavigationHidingStackViewमें जोड़ा जा सकता है. यह स्टैक व्यू, बटन को वर्टिकल तौर पर व्यवस्थित करता है. साथ ही, जगह कम होने पर, यह बटन कोarrangedSubviewsकलेक्शन के आखिर से छिपाना शुरू कर देता है.isHiddenको उन व्यू पर सेट न करें जिन्हें वह मैनेज करता है.
NavigationStyleValues.headerNominalHeight के आधार पर तय होती है. यह ऊंचाई, टर्न कार्ड के किसी भी कॉन्टेंट के लिए काफ़ी होती है. कैमरा फ़्रेमिंग को स्थिर रखने के लिए, viewport के सबसे ऊपरी हिस्से को सीधे turnCard के सबसे निचले हिस्से से जोड़ें. जैसा कि उदाहरण के तौर पर लागू करने में दिखाया गया है.
डाइनैमिक प्रॉम्प्ट कॉम्पोनेंट
घटना की सूचनाएं या सुरक्षा कैमरे की चेतावनियां जैसे प्रॉम्प्ट, ऐक्टिव गाइडेंस के दौरान अपने-आप ट्रिगर होते हैं.
जब कोई प्रॉम्प्ट दिखने के लिए तैयार होता है, तो SDK टूल आपके डेलिगेट के showPrompt(navMap:newPrompt:) कॉलबैक को कॉल करता है और सीधे newPrompt व्यू को पास करता है. आपका डेलिगेट, इस प्रॉम्प्ट को आपके लेआउट पर आसानी से पोज़िशन करने के लिए ज़िम्मेदार होता है. आम तौर पर, यह आपके मैप कंटेनर के सबसे नीचे वाले किनारे पर ऐंकर होता है.
इनकमिंग प्रॉम्प्ट, स्क्रीन के निचले हिस्से को कवर करता है. इसलिए, आपको अपना लेआउट अपडेट करना होगा, ताकि प्रॉम्प्ट, व्यूपोर्ट, Google के लोगो या सबसे नीचे मौजूद किसी भी बटन को ओवरलैप न करे.
आपका डेलिगेट यह भी कंट्रोल करता है कि स्क्रीन पर प्रॉम्प्ट कैसे दिखें और कैसे हटें. ज़्यादा जानकारी के लिए, प्रॉम्प्ट को पसंद के मुताबिक बनाना लेख पढ़ें.
स्क्रीन साइज़ और वाइड मोड को मैनेज करना
मैप के डाइमेंशन और स्क्रीन ओरिएंटेशन में होने वाले बदलावों को मैनेज करने के लिए, लेआउट डेलिगेट इन सुविधाओं का इस्तेमाल करता है:
व्यू का साइज़ बदलना: जब भी मैप का साइज़ बदलता है, तब यह आपके लेआउट को अडजस्ट करता है.
वाइड मोड: मैप के काफ़ी चौड़ा होने पर, यह वाइड-फ़ॉर्मैट वाले लेआउट वैरिएंट पर स्विच हो जाता है.
व्यू का साइज़ बदलने पर रिस्पॉन्स दे रहा है
स्प्लिट-स्क्रीन लेआउट, कंटेनर स्लाइडर, और डिवाइस को घुमाने से, आम तौर परsizeChanged(navMap:newState:) कॉलबैक ट्रिगर होता है. अपने कस्टम यूज़र इंटरफ़ेस (यूआई) में सामान्य रिस्पॉन्सिव अडजस्टमेंट करने के लिए, इस कॉलबैक का इस्तेमाल करें. नई स्क्रीन के आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) के लिए, कस्टम एलिमेंट की जगह बदलने के लिए sizeChanged(navMap:newState:) लागू करें. साथ ही, अपनी पसंद के मुताबिक चौड़ाई या ऊंचाई के ब्रेकपॉइंट लागू करें. इसके अलावा, यह पता लगाएं कि साइज़ बदलने वाला कोई इवेंट, वाइड मोड को कब टॉगल करता है, ताकि लेआउट के वैरिएंट को सुरक्षित तरीके से बदला जा सके.
वाइड मोड के बारे में जानकारी
वाइड मोड तब चालू होता है, जब मैप कंटेनर इतना चौड़ा हो कि यूज़र इंटरफ़ेस (यूआई) के कॉम्पोनेंट को साथ-साथ दिखाया जा सके.
कस्टम यूज़र इंटरफ़ेस (यूआई) एलिमेंट की जगह बदलने के लिए, स्टेट ऑब्जेक्ट से wideMode बूलियन को पढ़ें. साथ ही, ड्राइवर के लिए मैप के बीच के हिस्से को साफ़ रखें. Google के उपलब्ध कराए गए कॉम्पोनेंट, जैसे कि टर्न कार्ड और ईटीए कार्ड, वाइड मोड में अपने-आप छोटे हो जाते हैं और उनका आकार बदल जाता है. इसलिए, इस बूलियन को पढ़ने से यह पक्का होता है कि Google के कॉम्पोनेंट अपडेट होने के साथ ही, आपका लेआउट भी अडजस्ट हो जाए.
स्टैंडर्ड और वाइड मोड में लेआउट कॉम्पोनेंट को पोज़िशन करने के लिए, यहां दिए गए उदाहरण देखें:
स्टैंडर्ड पोर्ट्रेट मोड: इसमें टर्न कार्ड को स्क्रीन पर सबसे ऊपर और ईटीए कार्ड को सबसे नीचे रखा जाता है.
वाइड मोड: टर्न कार्ड और ईटीए कार्ड को स्क्रीन के सबसे आगे वाले किनारे पर रखें. इसमें टर्न कार्ड सबसे ऊपर और ईटीए कार्ड सबसे नीचे होना चाहिए. किसी भी कार्ड को लीडिंग एज से दूर रखने पर, अनचाहे नतीजे मिल सकते हैं. व्यूपोर्ट और अपने कॉन्टेंट को कहीं भी रखा जा सकता है. आम तौर पर, इन्हें कार्ड के आखिर में रखा जाता है.
स्टेट ट्रांज़िशन के दौरान वाइड मोड की जांच करना
अगर आपका लेआउट, वाइड-मोड वैरिएशन के साथ काम करता है, तो सिर्फ़ sizeChanged(navMap:newState:) पर भरोसा करने के बजाय, हर स्टेट ट्रांज़िशन कॉलबैक के अंदर wideMode बूलियन का आकलन करें.
enableNavigationUI(navMap:newState:) और startGuidance(navMap:newState:oldState:) जैसे कॉलबैक के दौरान, स्टेट ऑब्जेक्ट की जांच करें, ताकि लैंडस्केप ओरिएंटेशन में ऐप्लिकेशन के शुरुआती लॉन्च को मैनेज किया जा सके. इस तरीके से यह पक्का होता है कि आपका लेआउट, वाइड मोड की स्थिति का आकलन करे. साथ ही, नया नेविगेशन शुरू होते ही सही स्टैंडर्ड या वाइड लेआउट वैरिएंट को चालू करे.
Google की स्टाइल के मुताबिक बनाना
कस्टम व्यू को Google के कॉम्पोनेंट के स्पेसिंग से मैच करने के लिए, SDK टूल NavigationStyleValues क्लास उपलब्ध कराता है. इसकी क्लास प्रॉपर्टी, पॉइंट में मेज़रमेंट दिखाती हैं.
उदाहरण के लिए, टर्न कार्ड के सामने स्क्रीन के सबसे ऊपर कोने में कस्टम बटन जोड़ने के लिए, बटन के सबसे ऊपर मार्जिन के तौर पर NavigationStyleValues.headerTopPadding का इस्तेमाल करें. इसके बाद, बटन टर्न कार्ड के सबसे ऊपर वाले किनारे के साथ अलाइन हो जाता है.
ये मेज़रमेंट उपलब्ध हैं:
NavigationStyleValues.buttonHorizontalPaddingNavigationStyleValues.buttonVerticalPaddingNavigationStyleValues.defaultCornerRadiusNavigationStyleValues.elementHorizontalPaddingNavigationStyleValues.headerFooterHorizontalPaddingNavigationStyleValues.headerNominalHeightNavigationStyleValues.headerTopPadding
Google के उपलब्ध कराए गए व्यू में, पहले से मौजूद मार्जिन भी शामिल होते हैं. Google के दो व्यू को स्टैक करते समय, एक को दूसरे के layoutMarginsGuide तक सीमित करें, ताकि उनके मार्जिन आपस में न जुड़ें. उदाहरण के लिए, स्पीड विजेट को ईटीए कार्ड के ऊपर रखने के लिए, speedWidget के बॉटम ऐंकर को etaCard.layoutMarginsGuide.topAnchor पर सेट करें.
प्रॉम्प्ट को पसंद के मुताबिक बनाना
आपका लेआउट डेलिगेट यह कंट्रोल करता है कि प्रॉम्प्ट, स्क्रीन पर कैसे दिखें और कैसे हटें. साथ ही, उनके कौनसे कोने गोल हों.
प्रॉम्प्ट ऐनिमेट करना
SDK, आपके लिए प्रॉम्प्ट ऐनिमेट नहीं करता. आपका डेलिगेट यह तय करता है कि हर प्रॉम्प्ट कैसे दिखेगा और कैसे गायब होगा:
showPrompt(navMap:newPrompt:)में, मैप व्यू में शुरुआती पोज़िशन पर प्रॉम्प्ट जोड़ें. इसके बाद, उसे ऐनिमेट करके सही जगह पर ले जाएं. उदाहरण के लिए, प्रॉम्प्ट को दिखने वाले हिस्से के ठीक नीचे या बॉटम शीट के पीछे से शुरू करें. साथ ही, इसके ऊपरी किनारे को शीट के ऊपरी हिस्से के साथ अलाइन करें.hidePrompt(navMap:oldPrompt:)में, प्रॉम्प्ट को ऐनिमेट करके व्यू से बाहर करें. इसके बाद, ऐनिमेशन खत्म होने पर उसे सुपरव्यू से हटाएं.changePrompt(navMap:newPrompt:oldPrompt:)में, एक प्रॉम्प्ट को दूसरे प्रॉम्प्ट से बदलने के लिए, छिपाने और दिखाने वाले ऐनिमेशन एक साथ चलाएं.
लागू करने के तरीके के उदाहरण में इस तरीके का इस्तेमाल किया गया है.
ट्रैफ़िक, बेहतर रास्तों, और दुर्घटनाओं के बारे में सूचनाएं पाने की सुविधा बंद करने के लिए, GMSNavigator की shouldDisplayPrompts प्रॉपर्टी को false पर सेट करें.
प्रॉम्प्ट के कोनों को गोल करना
प्रॉम्प्ट, NavigationRoundedCornerView प्रोटोकॉल के मुताबिक होते हैं. इसलिए, आपके पास यह चुनने का विकल्प होता है कि उनके कौनसे कोने गोल हों. डिफ़ॉल्ट रूप से, प्रॉम्प्ट के सिर्फ़ ऊपरी कोने गोल होते हैं. यह स्क्रीन के सबसे निचले किनारे पर ऐंकर किए गए प्रॉम्प्ट के लिए सही होता है.
अगर प्रॉम्प्ट को किसी अन्य कॉन्टेंट के ऊपर रखा जाता है, जैसे कि बॉटम शीट, तो चारों कोनों को गोल करें:
newPrompt.roundedCorners = [
.layerMinXMinYCorner, .layerMaxXMinYCorner,
.layerMinXMaxYCorner, .layerMaxXMaxYCorner,
]
उदाहरण: कंस्ट्रेंट-आधारित लेआउट लागू करना
यहां दिए गए उदाहरण में, एक बेसिक लेआउट डेलिगेट दिखाया गया है. यह ऑटो लेआउट की पाबंदियों का इस्तेमाल करके, टर्न कार्ड, ईटीए कार्ड, बटन, Google के लोगो, और व्यूपोर्ट को पोज़िशन करता है. यह स्क्रीन पर प्रॉम्प्ट को स्लाइड करके दिखाता है और हटाता है. उदाहरण को छोटा रखने के लिए, इसमें वाइड मोड, स्पीड विजेट या यात्रा की प्रोग्रेस बार को हैंडल नहीं किया जाता. इसलिए, यह सिर्फ़ पोर्ट्रेट ओरिएंटेशन के साथ काम करने वाले ऐप्लिकेशन के लिए सही है.
import GoogleNavigation
import UIKit
/// A layout delegate that positions the essential navigation UI elements: the
/// turn card, ETA card, buttons, Google logo, and viewport. It also slides
/// prompts on and off the screen.
@MainActor
final class BasicLayoutDelegate: NSObject, NavigationLayoutDelegate {
/// How long prompts take to slide on or off the screen, in seconds.
private static let promptAnimationDuration: TimeInterval = 0.4
/// The views and constraints for one navigation state.
@MainActor
private final class StateElements {
let buttonStack = GMSNavigationHidingStackView(
position: .bottom, alignment: .trailing)
var constraints: [NSLayoutConstraint] = []
private unowned let navMap: GMSMapView
private let buttons: [UIView]
private let topLevelViews: [UIView]
init(
navMap: GMSMapView, buttons: [NavigationUIButton],
topLevelViews: [UIView]
) {
self.navMap = navMap
self.buttons = buttons.map { $0.view }
self.topLevelViews = topLevelViews
}
/// Adds the views to the map view, and then activates the constraints.
func activate() {
topLevelViews.forEach { navMap.addSubview($0) }
buttonStack.arrangedSubviews = buttons
navMap.addSubview(buttonStack)
NSLayoutConstraint.activate(constraints)
}
/// Deactivates the constraints, and then removes the views.
func deactivate() {
NSLayoutConstraint.deactivate(constraints)
buttonStack.removeFromSuperview()
buttonStack.arrangedSubviews = []
topLevelViews.forEach { $0.removeFromSuperview() }
}
}
private var navigationReadyElements: StateElements?
private var activeGuidanceElements: StateElements?
private var currentPrompt: NavigationPromptView?
private var promptBottomConstraint: NSLayoutConstraint?
/// Constraints that apply in both the navigation-ready and active guidance
/// states.
private var sharedConstraints: [NSLayoutConstraint] = []
/// Marks the bottom of the map content area. The guide stays above the ETA
/// card and above any prompt that's on the screen.
private let mapContentsBottomGuide = UILayoutGuide()
// MARK: - Navigation-ready state
func enableNavigationUI(
navMap: GMSMapView, newState: NavigationReadyUIState
) {
let safeArea = navMap.safeAreaLayoutGuide
let bottomGuide = mapContentsBottomGuide
navMap.addLayoutGuide(bottomGuide)
// Keep the guide at the bottom of the safe area unless a higher-priority
// constraint pushes it up.
let lowPriorityBottom = bottomGuide.bottomAnchor.constraint(
equalTo: safeArea.bottomAnchor)
lowPriorityBottom.priority = .defaultLow
sharedConstraints = [
bottomGuide.heightAnchor.constraint(equalToConstant: 0),
lowPriorityBottom,
]
let logo = newState.googleLogo
let viewport = newState.viewport
let elements = StateElements(
navMap: navMap, buttons: newState.buttons,
topLevelViews: [logo, viewport])
let buttonStack = elements.buttonStack
elements.constraints = sharedConstraints + [
buttonStack.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor),
buttonStack.topAnchor.constraint(equalTo: safeArea.topAnchor),
buttonStack.bottomAnchor.constraint(equalTo: bottomGuide.topAnchor),
logo.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
logo.bottomAnchor.constraint(equalTo: bottomGuide.topAnchor),
viewport.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
viewport.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor),
viewport.topAnchor.constraint(equalTo: safeArea.topAnchor),
viewport.bottomAnchor.constraint(equalTo: bottomGuide.topAnchor),
]
elements.activate()
navigationReadyElements = elements
}
func disableNavigationUI(
navMap: GMSMapView, oldState: NavigationReadyUIState
) {
currentPrompt?.removeFromSuperview()
currentPrompt = nil
promptBottomConstraint = nil
navigationReadyElements?.deactivate()
navigationReadyElements = nil
navMap.removeLayoutGuide(mapContentsBottomGuide)
}
// MARK: - Active guidance state
func startGuidance(
navMap: GMSMapView, newState: ActiveGuidanceUIState,
oldState: NavigationReadyUIState
) {
navigationReadyElements?.deactivate()
let safeArea = navMap.safeAreaLayoutGuide
let bottomGuide = mapContentsBottomGuide
let turnCard = newState.turnCard
let etaCard = newState.etaCard
let logo = newState.googleLogo
let viewport = newState.viewport
let elements = StateElements(
navMap: navMap, buttons: newState.buttons,
topLevelViews: [turnCard, etaCard, viewport, logo])
let buttonStack = elements.buttonStack
elements.constraints = sharedConstraints + [
turnCard.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
turnCard.topAnchor.constraint(equalTo: navMap.topAnchor),
etaCard.leadingAnchor.constraint(equalTo: navMap.leadingAnchor),
etaCard.bottomAnchor.constraint(equalTo: navMap.bottomAnchor),
bottomGuide.bottomAnchor.constraint(
lessThanOrEqualTo: etaCard.topAnchor),
buttonStack.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor),
buttonStack.topAnchor.constraint(equalTo: turnCard.bottomAnchor),
buttonStack.bottomAnchor.constraint(equalTo: bottomGuide.topAnchor),
logo.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
logo.bottomAnchor.constraint(equalTo: bottomGuide.topAnchor),
// Frame the camera in the open area below the turn card.
viewport.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
viewport.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor),
viewport.topAnchor.constraint(equalTo: turnCard.bottomAnchor),
viewport.bottomAnchor.constraint(equalTo: bottomGuide.topAnchor),
]
elements.activate()
activeGuidanceElements = elements
}
func stopGuidance(
navMap: GMSMapView, newState: NavigationReadyUIState,
oldState: ActiveGuidanceUIState
) {
activeGuidanceElements?.deactivate()
activeGuidanceElements = nil
navigationReadyElements?.activate()
}
// MARK: - Prompts
func showPrompt(navMap: GMSMapView, newPrompt: NavigationPromptView) {
navMap.addSubview(newPrompt)
currentPrompt = newPrompt
// Start with the prompt just below the bottom edge of the map view.
let bottomConstraint = newPrompt.bottomAnchor.constraint(
equalTo: navMap.bottomAnchor, constant: newPrompt.bounds.height)
promptBottomConstraint = bottomConstraint
NSLayoutConstraint.activate([
bottomConstraint,
newPrompt.leadingAnchor.constraint(
equalTo: navMap.safeAreaLayoutGuide.leadingAnchor),
// Keep the map content above the prompt.
mapContentsBottomGuide.bottomAnchor.constraint(
lessThanOrEqualTo: newPrompt.topAnchor),
])
// Lay out the prompt in its starting position, and then slide it up.
navMap.layoutIfNeeded()
UIView.animate(withDuration: Self.promptAnimationDuration) {
bottomConstraint.constant = 0
navMap.layoutIfNeeded()
}
}
func hidePrompt(navMap: GMSMapView, oldPrompt: NavigationPromptView) {
guard let bottomConstraint = promptBottomConstraint else { return }
promptBottomConstraint = nil
currentPrompt = nil
// Slide the prompt down, and then remove it.
UIView.animate(withDuration: Self.promptAnimationDuration) {
bottomConstraint.constant = oldPrompt.bounds.height
navMap.layoutIfNeeded()
} completion: { _ in
oldPrompt.removeFromSuperview()
}
}
func changePrompt(
navMap: GMSMapView, newPrompt: NavigationPromptView,
oldPrompt: NavigationPromptView
) {
// Slide the old prompt out and the new prompt in at the same time.
hidePrompt(navMap: navMap, oldPrompt: oldPrompt)
showPrompt(navMap: navMap, newPrompt: newPrompt)
}
}
डेलिगेट का इस्तेमाल करने के लिए, नेविगेशन चालू करने से पहले उसे अपने मैप व्यू पर सेट करें. इसके बारे में ज़रूरी चेकलिस्ट में बताया गया है.
सीमाएं और आम तौर पर होने वाली समस्याएं
लेआउट को पसंद के मुताबिक बनाने की सुविधा, CarPlay पर लागू नहीं होती. इसमें तय किए गए लेआउट टेंप्लेट का इस्तेमाल किया जाता है. अगर CarPlay स्क्रीन के लिए बनाए गए मैप व्यू पर setLayoutDelegate(_:) को कॉल किया जाता है, तो यह तरीका गड़बड़ी दिखाता है.
एसडीके के कुछ वर्शन पर असर डालने वाली समस्याओं के बारे में जानने के लिए, रिलीज़ नोट में जानी-पहचानी समस्याएं सेक्शन देखें.