SwiftUI (Swift) का इस्तेमाल करके, अपने iOS ऐप्लिकेशन में मैप जोड़ना

1. शुरू करने से पहले

इस कोडलैब में, SwiftUI के साथ iOS के लिए Maps SDK का इस्तेमाल करने का तरीका बताया गया है.

screenshot-iphone-12-black@2x.png

ज़रूरी शर्तें

  • Swift की बुनियादी जानकारी
  • SwiftUI के बारे में बुनियादी जानकारी

आपको क्या करना होगा

  • SwiftUI का इस्तेमाल करके, iOS ऐप्लिकेशन में Google Maps जोड़ने के लिए, iOS के लिए Maps SDK को चालू करें और उसका इस्तेमाल करें.
  • मैप में मार्कर जोड़ें.
  • SwiftUI और GMSMapView ऑब्जेक्ट के बीच स्थिति को पास करें.

आपको किन चीज़ों की ज़रूरत होगी

2. सेट अप करें

इसके बाद, iOS के लिए Maps SDK चालू करें.

Google Maps Platform सेट अप करना

अगर आपके पास Google Cloud Platform खाता और बिलिंग की सुविधा वाला प्रोजेक्ट नहीं है, तो बिलिंग की सुविधा वाला खाता और प्रोजेक्ट बनाएं. ऐसा करने का तरीका जानने के लिए, कृपया Google Maps Platform का इस्तेमाल शुरू करना देखें.

  1. Cloud Console में, प्रोजेक्ट वाले ड्रॉप-डाउन मेन्यू पर क्लिक करें. इसके बाद, उस प्रोजेक्ट को चुनें जिसे इस कोडलैब के लिए इस्तेमाल करना है.

  1. इस कोडलैब के लिए ज़रूरी Google Maps Platform API और एसडीके को Google Cloud Marketplace में जाकर चालू करें. ऐसा करने के लिए, इस वीडियो या दस्तावेज़ में बताया गया तरीका अपनाएं.
  2. Cloud Console के क्रेडेंशियल पेज पर जाकर, एक एपीआई पासकोड जनरेट करें. ऐसा करने के लिए, इस वीडियो या दस्तावेज़ में बताया गया तरीका अपनाएं. Google Maps Platform का इस्तेमाल करने के लिए, एपीआई पासकोड ज़रूरी है.

3. स्टार्टर कोड डाउनलोड करना

इस कोडलैब को जल्द से जल्द शुरू करने के लिए, यहां कुछ शुरुआती कोड दिए गए हैं. आपके पास सीधे समाधान पर जाने का विकल्प है. हालांकि, अगर आपको इसे खुद बनाने के लिए सभी चरणों का पालन करना है, तो पढ़ना जारी रखें.

  1. अगर आपने git इंस्टॉल किया है, तो रिपॉज़िटरी को क्लोन करें.
git clone https://github.com/googlecodelabs/maps-ios-swiftui.git

इसके अलावा, सोर्स कोड को डाउनलोड करने के लिए, इस बटन पर क्लिक करें.

  1. कोड मिलने के बाद, टर्मिनल में cd डायरेक्ट्री में starter/GoogleMapsSwiftUI डालें.
  2. iOS के लिए Maps SDK टूल डाउनलोड करने के लिए, carthage update --platform iOS चलाएं
  3. आखिर में, GoogleMapsSwiftUI.xcodeproj फ़ाइल को Xcode में खोलें

4. कोड के बारे में खास जानकारी

आपने जो स्टार्टर प्रोजेक्ट डाउनलोड किया है उसमें ये क्लास पहले से मौजूद हैं और इन्हें लागू किया गया है:

  • AppDelegate - ऐप्लिकेशन का UIApplicationDelegate. Maps SDK for iOS को यहां शुरू किया जाएगा.
  • City - यह एक स्ट्रक्चर है, जो किसी शहर को दिखाता है. इसमें शहर का नाम और उसके निर्देशांक शामिल होते हैं.
  • MapViewController - यह एक स्कोप-डाउन UIKit UIViewController है, जिसमें Google Map (GMSMapView)
      शामिल है
    • SceneDelegate - ऐप्लिकेशन का UIWindowSceneDelegate, जिससे ContentView इंस्टैंटिएट किया जाता है.

इसके अलावा, यहां दी गई क्लास में कुछ फ़ंक्शन पहले से लागू हैं. आपको इस कोडलैब के आखिर तक, इन क्लास में बाकी फ़ंक्शन लागू करने होंगे:

  • ContentView - यह आपके ऐप्लिकेशन को शामिल करने वाला टॉप-लेवल का SwiftUI व्यू है.
  • MapViewControllerBridge - यह एक ऐसी क्लास है जो UIKit व्यू को SwiftUI व्यू से जोड़ती है. खास तौर पर, यह ऐसी क्लास है जो SwiftUI में MapViewController को ऐक्सेस करने की सुविधा देगी.

5. SwiftUI बनाम UIKit

SwiftUI को iOS 13 में, iOS ऐप्लिकेशन डेवलप करने के लिए UIKit के विकल्प के तौर पर पेश किया गया था. SwiftUI, अपने पुराने वर्शन UIKit की तुलना में कई फ़ायदे देता है. इनमें से कुछ के नाम यहां दिए गए हैं:

  • स्थिति बदलने पर, व्यू अपने-आप अपडेट हो जाते हैं. State नाम के ऑब्जेक्ट का इस्तेमाल करके, इसमें मौजूद वैल्यू में कोई भी बदलाव करने पर यूज़र इंटरफ़ेस (यूआई) अपने-आप अपडेट हो जाएगा.
  • लाइव प्रीव्यू की मदद से, तेज़ी से डेवलपमेंट किया जा सकता है. लाइव झलक की सुविधा से, कोड को बनाने और उसे एम्युलेटर पर डिप्लॉय करने की ज़रूरत कम हो जाती है. ऐसा इसलिए, क्योंकि SwiftUI व्यू की झलक को Xcode पर आसानी से देखा जा सकता है.
  • सोर्स-ऑफ़-ट्रुथ, Swift में है. SwiftUI में सभी व्यू, Swift में डिक्लेयर किए जाते हैं. इसलिए, अब Interface Builder का इस्तेमाल करना ज़रूरी नहीं है.
  • यह UIKit के साथ काम करता है. UIKit के साथ इंटरऑपरेबिलिटी की सुविधा होने से, मौजूदा ऐप्लिकेशन अपने मौजूदा व्यू के साथ-साथ, SwiftUI का इस्तेमाल कर सकते हैं. इसके अलावा, जिन लाइब्रेरी में अब तक SwiftUI का इस्तेमाल नहीं किया जा सकता, जैसे कि Maps SDK for iOS, उन्हें अब भी SwiftUI में इस्तेमाल किया जा सकता है.

इसके कुछ नुकसान भी हैं:

  • SwiftUI सिर्फ़ iOS 13 या उसके बाद के वर्शन पर उपलब्ध है.
  • Xcode की झलक में व्यू हैरारकी की जांच नहीं की जा सकती.

SwiftUI स्टेट और डेटा फ़्लो

SwiftUI, यूज़र इंटरफ़ेस (यूआई) बनाने का एक नया तरीका है. इसमें डिक्लेरेटिव अप्रोच का इस्तेमाल किया जाता है. इसमें आपको SwiftUI को यह बताना होता है कि आपको अपना व्यू कैसा दिखाना है. साथ ही, इसके लिए अलग-अलग स्थितियां भी बतानी होती हैं. इसके बाद, सिस्टम बाकी काम अपने-आप कर देता है. SwiftUI, इवेंट या उपयोगकर्ता की कार्रवाई की वजह से, व्यू में होने वाले बदलावों को अपने-आप अपडेट करता है. इस डिज़ाइन को आम तौर पर एकतरफ़ा डेटा फ़्लो कहा जाता है. इस कोडलैब में, इस डिज़ाइन के बारे में ज़्यादा जानकारी नहीं दी गई है. हालांकि, हमारा सुझाव है कि आप Apple के स्टेट और डेटा फ़्लो के दस्तावेज़ में, इसके काम करने के तरीके के बारे में पढ़ें.

UIViewRepresentable या UIViewControllerRepresentable का इस्तेमाल करके, UIKit और SwiftUI को ब्रिज करना

iOS के लिए Maps SDK को UIKit के आधार पर बनाया गया है. साथ ही, यह SwiftUI के साथ काम करने वाला व्यू नहीं देता है. इसलिए, SwiftUI में इसका इस्तेमाल करने के लिए, UIViewRepresentable या UIViewControllerRepresentable में से किसी एक का पालन करना ज़रूरी है. इन प्रोटोकॉल की मदद से, SwiftUI में UIKit से बनाए गए UIView और UIViewController शामिल किए जा सकते हैं. SwiftUI व्यू में Google Maps जोड़ने के लिए, दोनों प्रोटोकॉल का इस्तेमाल किया जा सकता है. हालांकि, अगले चरण में हम मैप वाले UIViewController को शामिल करने के लिए, UIViewControllerRepresentable का इस्तेमाल करने के बारे में जानेंगे.

6. मैप जोड़ना

इस सेक्शन में, Google Maps को SwiftUI व्यू में जोड़ा जाएगा.

add-a-map-screenshot@2x.png

अपनी एपीआई कुंजी जोड़ना

पिछले चरण में बनाई गई एपीआई कुंजी को Maps SDK for iOS को देना होगा, ताकि आपके खाते को उस मैप से जोड़ा जा सके जो ऐप्लिकेशन पर दिखेगा.

अपनी एपीआई कुंजी देने के लिए, AppDelegate.swift फ़ाइल खोलें और application(_, didFinishLaunchingWithOptions) तरीके पर जाएं. SDK टूल को GMSServices.provideAPIKey() का इस्तेमाल करके, "YOUR_API_KEY" स्ट्रिंग के साथ शुरू किया जाता है. उस स्ट्रिंग की जगह अपना एपीआई पासकोड डालें. इस चरण को पूरा करने से, ऐप्लिकेशन लॉन्च होने पर iOS के लिए Maps SDK शुरू हो जाएगा.

MapViewControllerBridge का इस्तेमाल करके Google मैप जोड़ना

एसडीके को एपीआई पासकोड देने के बाद, अगला चरण ऐप्लिकेशन पर मैप दिखाना है.

स्टार्टर कोड में दिए गए व्यू कंट्रोलर, MapViewController के व्यू में GMSMapView शामिल होता है. हालांकि, इस व्यू कंट्रोलर को UIKit में बनाया गया था. इसलिए, आपको इस क्लास को SwiftUI में ब्रिज करना होगा, ताकि इसका इस्तेमाल ContentView में किया जा सके. इसके लिए:

  1. Xcode में MapViewControllerBridge फ़ाइल खोलें.

यह क्लास, UIViewControllerRepresentable के मुताबिक है. यह एक ऐसा प्रोटोकॉल है जिसकी ज़रूरत UIKit UIViewController को रैप करने के लिए होती है, ताकि इसे SwiftUI व्यू के तौर पर इस्तेमाल किया जा सके. दूसरे शब्दों में कहें, तो इस प्रोटोकॉल का पालन करने से, UIKit व्यू को SwiftUI व्यू से जोड़ने में आसानी होती है. इस प्रोटोकॉल का पालन करने के लिए, दो तरीकों को लागू करना ज़रूरी है:

  • makeUIViewController(context) - इस तरीके को SwiftUI, UIViewController बनाने के लिए कॉल करता है. यहां आपको अपने UIViewController को इंस्टैंशिएट करना होगा और उसे उसकी शुरुआती स्थिति देनी होगी.
  • updateUIViewController(_, context) - जब भी स्थिति बदलती है, तब SwiftUI इस तरीके को कॉल करता है. स्टेट में बदलाव होने पर, UIViewController में कोई भी बदलाव यहां किया जा सकता है.
  1. 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) {
  }
}

ContentView में MapViewControllerBridge का इस्तेमाल करना

अब MapViewControllerBridge, MapViewController का इंस्टेंस बना रहा है. इसलिए, अगला चरण इस स्ट्रक्ट का इस्तेमाल ContentView में करके, मैप दिखाना है.

  1. Xcode में ContentView फ़ाइल खोलें.

ContentView को SceneDelegate में इंस्टैंशिएट किया जाता है. इसमें ऐप्लिकेशन का टॉप-लेवल व्यू होता है. इस फ़ाइल से मैप जोड़ा जाएगा.

  1. body प्रॉपर्टी में MapViewControllerBridge बनाएं.

इस फ़ाइल की body प्रॉपर्टी में, आपके लिए ZStack पहले से ही उपलब्ध है और इसे लागू किया जा चुका है. ZStack में शहरों की एक ऐसी सूची होती है जिसे इंटरैक्ट किया जा सकता है और ड्रैग किया जा सकता है. इसका इस्तेमाल आपको बाद के चरण में करना होगा. फ़िलहाल, ZStack के अंदर MapViewControllerBridge को ZStack के पहले चाइल्ड व्यू के तौर पर बनाएं, ताकि शहरों की सूची के पीछे ऐप्लिकेशन में मैप दिखे. ऐसा करने पर, ContentView में मौजूद 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()
      } // ...
    }
  }
}
  1. अब ऐप्लिकेशन चलाएं. अब आपको अपने डिवाइस की स्क्रीन पर मैप लोड होता हुआ दिखेगा. साथ ही, स्क्रीन पर सबसे नीचे शहरों की एक ऐसी सूची दिखेगी जिसे खींचकर ऊपर-नीचे किया जा सकता है.

7. मैप में मार्कर जोड़ना

पिछले चरण में, आपने शहरों की सूची दिखाने वाले इंटरैक्टिव मैप को जोड़ा था. इस सेक्शन में, आपको सूची में मौजूद हर शहर के लिए मार्कर जोड़ने होंगे.

map-with-markers@2x.png

मार्कर को राज्य के तौर पर इस्तेमाल करना

ContentView, markers नाम की एक प्रॉपर्टी का एलान करता है. यह GMSMarker की एक सूची है. इसमें cities स्टैटिक प्रॉपर्टी में एलान किए गए हर शहर को दिखाया गया है. ध्यान दें कि इस प्रॉपर्टी को SwiftUI प्रॉपर्टी रैपर State के साथ एनोटेट किया गया है. इससे पता चलता है कि इसे 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)
      }
    }
  }
}

@Binding का इस्तेमाल करके, MapViewControllerBridge को State पास करें

markers प्रॉपर्टी से डेटा दिखाने वाले शहरों की सूची के अलावा, इस प्रॉपर्टी को MapViewControllerBridge स्ट्रक्ट में पास करें, ताकि इसका इस्तेमाल मैप पर उन मार्कर को दिखाने के लिए किया जा सके. ऐसा करने के लिए:

  1. MapViewControllerBridge में नई markers प्रॉपर्टी का एलान करें, जिसमें @Binding एनोटेशन जोड़ा गया हो

MapViewControllerBridge

struct MapViewControllerBridge: : UIViewControllerRepresentable {
  @Binding var markers: [GMSMarker]
  // ...
}
  1. MapViewControllerBridge में, markers प्रॉपर्टी का इस्तेमाल करने के लिए, updateUIViewController(_, context) तरीका अपडेट करें

पिछले चरण में बताया गया है कि जब भी स्थिति में बदलाव होगा, तब SwiftUI, updateUIViewController(_, context) को कॉल करेगा. हमें इस तरीके में मैप को अपडेट करना है, ताकि 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 }
  }
}
  1. 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 प्रॉपर्टी रैपर के साथ इस्तेमाल करने के लिए रिज़र्व किया गया प्रीफ़िक्स है. किसी स्टेट पर लागू करने पर, यह Binding दिखाता है.

  1. अब ऐप्लिकेशन चलाकर देखें कि मैप पर मार्कर दिख रहे हैं या नहीं.

8. चुने गए शहर पर ऐनिमेशन दिखाना

पिछले चरण में, आपने एक SwiftUI व्यू से दूसरे SwiftUI व्यू में राज्य की जानकारी भेजकर, मैप में मार्कर जोड़े थे. इस चरण में, इंटरैक्ट की जा सकने वाली सूची में किसी शहर या मार्कर पर टैप करने के बाद, उसे ऐनिमेट किया जाएगा. ऐनिमेशन बनाने के लिए, आपको किसी स्टेट में हुए बदलावों पर प्रतिक्रिया देनी होगी. इसके लिए, बदलाव होने पर मैप की कैमरा पोज़िशन में बदलाव करें. मैप के कैमरे के कॉन्सेप्ट के बारे में ज़्यादा जानने के लिए, कैमरा और व्यू देखें.

animate-city@2x.png

मैप को चुने गए शहर पर ऐनिमेट करें

मैप को किसी चुने गए शहर पर ऐनिमेट करने के लिए:

  1. MapViewControllerBridge में नया बाइंडिंग तय करना

ContentView में selectedMarker नाम की एक स्टेट प्रॉपर्टी है. इसे शून्य पर सेट किया गया है. जब भी सूची में कोई शहर चुना जाता है, तब यह अपडेट हो जाती है. इसे ContentView में मौजूद CitiesList व्यू buttonAction मैनेज करता है.

ContentView

CitiesList(markers: $markers) { (marker) in
  guard self.selectedMarker != marker else { return }
  self.selectedMarker = marker
  // ...
}

जब भी selectedMarker में बदलाव होता है, तो MapViewControllerBridge को इस बदलाव के बारे में पता होना चाहिए, ताकि वह मैप को चुने गए मार्कर पर ऐनिमेशन के साथ दिखा सके. इसलिए, GMSMarker टाइप का नया Binding, MapViewControllerBridge में तय करें और प्रॉपर्टी का नाम selectedMarker रखें.

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  @Binding var selectedMarker: GMSMarker?
}
  1. selectedMarker में बदलाव होने पर, मैप को ऐनिमेट करने के लिए MapViewControllerBridge को अपडेट करें

नया बाइंडिंग घोषित होने के बाद, आपको MapViewControllerBridge के 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 के animate(with) फ़ंक्शन का इस्तेमाल करके, मैप के ऐनिमेशन का क्रम दिखाएगा.

  1. ContentView का selectedMarker, MapViewControllerBridge को पास करें

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, व्यू को ऐनिमेट करने की प्रोसेस को आसान बनाता है. ऐसा इसलिए, क्योंकि यह स्टेट ट्रांज़िशन के लिए ऐनिमेशन को मैनेज करता है. इसे दिखाने के लिए, मैप ऐनिमेशन पूरा होने के बाद, चुनी गई सिटी पर व्यू को फ़ोकस करके ज़्यादा ऐनिमेशन जोड़े जाएंगे. ऐसा करने के लिए, यहां दिया गया तरीका अपनाएं:

  1. MapViewControllerBridge में onAnimationEnded क्लोज़र जोड़ना

SwiftUI ऐनिमेशन, मैप ऐनिमेशन के उस क्रम के बाद होगा जिसे आपने पहले जोड़ा था. इसलिए, MapViewControllerBridge के अंदर onAnimationEnded नाम का एक नया क्लोज़र घोषित करें. साथ ही, animateToSelectedMarker(viewController) तरीके के अंदर, मैप ऐनिमेशन के खत्म होने के 0.5 सेकंड बाद इस क्लोज़र को शुरू करें.

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()
            })
          })
        }
      }
    }
  }
}
  1. MapViewControllerBridge में onAnimationEnded लागू करना

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))
      }
    }
  }
}
  1. ऐनिमेशन देखने के लिए, ऐप्लिकेशन चलाएं!

9. SwiftUI को इवेंट भेजना

इस चरण में, आपको GMSMapView से भेजे गए इवेंट सुनने होंगे. साथ ही, उस इवेंट को SwiftUI पर भेजना होगा. खास तौर पर, आपको मैप व्यू के लिए एक डेलिगेट सेट करना होगा. साथ ही, कैमरे के मूव होने से जुड़े इवेंट सुनने होंगे. इससे, जब किसी शहर पर फ़ोकस किया जाएगा और मैप का कैमरा किसी जेस्चर से मूव होगा, तो मैप व्यू अनफ़ोकस हो जाएगा. इससे आपको मैप का ज़्यादा हिस्सा दिखेगा.

SwiftUI कोऑर्डिनेटर का इस्तेमाल करना

GMSMapView, कैमरे की पोज़िशन में बदलाव या मार्कर पर टैप करने जैसे इवेंट जनरेट करता है. इन इवेंट को सुनने का तरीका, GMSMapViewDelegate प्रोटोकॉल के ज़रिए होता है. SwiftUI, Coordinator का कॉन्सेप्ट पेश करता है. इसका इस्तेमाल खास तौर पर, UIKit व्यू कंट्रोलर के लिए डेलिगेट के तौर पर काम करने के लिए किया जाता है. इसलिए, SwiftUI में Coordinator को GMSMapViewDelegate प्रोटोकॉल के मुताबिक काम करना चाहिए. इसके लिए, यह तरीका अपनाएं:

  1. MapViewControllerBridge में MapViewCoordinator नाम का कोऑर्डिनेटर बनाएं

MapViewControllerBridge क्लास में एक नेस्ट की गई क्लास बनाएं और उसे MapViewCoordinator नाम दें. इस क्लास को GMSMapViewDelegate के मुताबिक होना चाहिए. साथ ही, इसे MapViewControllerBridge को प्रॉपर्टी के तौर पर एलान करना चाहिए.

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  final class MapViewCoordinator: NSObject, GMSMapViewDelegate {
    var mapViewControllerBridge: MapViewControllerBridge

    init(_ mapViewControllerBridge: MapViewControllerBridge) {
      self.mapViewControllerBridge = mapViewControllerBridge
    }
  }
}
  1. MapViewControllerBridge में makeCoordinator() लागू करना

इसके बाद, MapViewControllerBridge में makeCoordinator() तरीके को लागू करें और पिछले चरण में बनाए गए MapViewCoodinator का एक इंस्टेंस वापस पाएं.

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  func makeCoordinator() -> MapViewCoordinator {
    return MapViewCoordinator(self)
  }
}
  1. MapViewCoordinator को मैप व्यू के डेलिगेट के तौर पर सेट करें

कस्टम कोऑर्डिनेटर बनाने के बाद, अगला चरण कोऑर्डिनेटर को व्यू कंट्रोलर के मैप व्यू के लिए डेलिगेट के तौर पर सेट करना है. इसके लिए, makeUIViewController(context) में व्यू कंट्रोलर के इनिशियलाइज़ेशन को अपडेट करें. पिछले चरण में बनाया गया कोऑर्डिनेटर, कॉन्टेक्स्ट ऑब्जेक्ट से ऐक्सेस किया जा सकेगा.

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  func makeUIViewController(context: Context) -> MapViewController {
    let uiViewController = MapViewController()
    uiViewController.map.delegate = context.coordinator
    return uiViewController
  }
}
  1. 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)
    }
  }
}
  1. mapWillMove के लिए वैल्यू पास करने के लिए, ContentView को अपडेट करें

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
        })
        // ...
      }
    }
  }
}
  1. नए बदलाव देखने के लिए, ऐप्लिकेशन को चलाएं!

10. बधाई हो

यहां तक पहुंचने के लिए बधाई! आपने काफ़ी कुछ सीख लिया है. हमें उम्मीद है कि अब आपको iOS के लिए Maps SDK का इस्तेमाल करके, अपना SwiftUI ऐप्लिकेशन बनाने में मदद मिलेगी.

आपने क्या सीखा

  • SwiftUI और UIKit में अंतर
  • UIViewControllerRepresentable का इस्तेमाल करके, SwiftUI और UIKit के बीच ब्रिज बनाने का तरीका
  • State और Binding की मदद से, मैप व्यू में बदलाव करने का तरीका
  • Coordinator का इस्तेमाल करके, मैप व्यू से SwiftUI को इवेंट भेजने का तरीका

आगे क्या करना है?

  • iOS के लिए Maps SDK
    • iOS के लिए Maps SDK का आधिकारिक दस्तावेज़
  • Places SDK for iOS - अपने आस-पास के स्थानीय कारोबारों और दिलचस्पी की जगहों के बारे में जानकारी पाना
  • maps-sdk-for-ios-samples
    • GitHub पर सैंपल कोड, जिसमें iOS के लिए Maps SDK की सभी सुविधाओं के बारे में बताया गया है.
  • SwiftUI - SwiftUI के बारे में Apple का आधिकारिक दस्तावेज़
  • नीचे दिए गए सर्वे के सवालों के जवाब दें, ताकि हम आपके लिए सबसे काम का कॉन्टेंट बना सकें:

आपको और कौनसे कोडलैब देखने हैं?

मैप पर डेटा विज़ुअलाइज़ेशन अपने मैप की स्टाइल को पसंद के मुताबिक बनाने के बारे में ज़्यादा जानकारी मैप में 3D इंटरैक्शन के लिए बिल्डिंग

क्या आपको अपनी पसंद का कोडलैब नहीं मिल रहा है? यहां नई समस्या के साथ इसका अनुरोध करें.