با SwiftUI (Swift) یک نقشه به برنامه iOS خود اضافه کنید

1. قبل از شروع

این کد لبه به شما می آموزد که چگونه از Maps SDK برای iOS با SwiftUI استفاده کنید.

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

پیش نیازها

  • دانش اولیه سوئیفت
  • آشنایی اولیه با SwiftUI

کاری که خواهی کرد

  • Maps SDK برای iOS را فعال کنید و از آن استفاده کنید تا Google Maps را با استفاده از SwiftUI به برنامه iOS اضافه کنید.
  • نشانگرها را به نقشه اضافه کنید.
  • وضعیت را از نمای SwiftUI به یک شی GMSMapView و بالعکس.

آنچه شما نیاز دارید

2. راه اندازی شوید

برای مرحله فعال سازی زیر، Maps SDK برای iOS را فعال کنید.

پلتفرم نقشه های گوگل را راه اندازی کنید

اگر قبلاً حساب Google Cloud Platform و پروژه‌ای با صورت‌حساب فعال ندارید، لطفاً راهنمای شروع به کار با Google Maps Platform را برای ایجاد یک حساب صورت‌حساب و یک پروژه ببینید.

  1. در Cloud Console ، روی منوی کشویی پروژه کلیک کنید و پروژه ای را که می خواهید برای این کد لبه استفاده کنید انتخاب کنید.

  1. APIها و SDKهای پلتفرم Google Maps مورد نیاز برای این لبه کد را در Google Cloud Marketplace فعال کنید. برای انجام این کار، مراحل این ویدئو یا این مستند را دنبال کنید.
  2. یک کلید API در صفحه Credentials در Cloud Console ایجاد کنید. می توانید مراحل این ویدئو یا این مستند را دنبال کنید. همه درخواست‌ها به پلتفرم نقشه‌های Google به یک کلید API نیاز دارند.

3. کد شروع را دانلود کنید

برای شروع هر چه سریع‌تر، در اینجا چند کد شروع وجود دارد که به شما کمک می‌کند تا این نرم‌افزار را دنبال کنید. از شما استقبال می شود که به سراغ راه حل بروید، اما اگر می خواهید تمام مراحل ساخت آن را خودتان دنبال کنید، به خواندن ادامه دهید.

  1. اگر git را نصب کرده اید، مخزن را کلون کنید.
git clone https://github.com/googlecodelabs/maps-ios-swiftui.git

همچنین می‌توانید روی دکمه زیر کلیک کنید تا کد منبع را دانلود کنید.

  1. پس از دریافت کد، در یک cd ترمینال وارد فهرست راهنما starter/GoogleMapsSwiftUI شوید.
  2. برای دانلود Maps SDK برای iOS، به carthage update --platform iOS را اجرا کنید
  3. در نهایت فایل GoogleMapsSwiftUI.xcodeproj را در Xcode باز کنید

4. مروری بر کد

در پروژه استارتری که دانلود کردید، کلاس های زیر برای شما تهیه و پیاده سازی شده است:

  • AppDelegate - UIApplicationDelegate برنامه. اینجاست که Maps SDK برای iOS راه اندازی می شود.
  • City - ساختاری که نشان دهنده یک شهر است (شامل نام و مختصات شهر).
  • MapViewController - یک UIKit UIViewController ساده حاوی نقشه گوگل ( GMSMapView )
  • SceneDelegate - UIWindowSceneDelegate برنامه که UIWindowSceneDelegate از آن نمونه سازی می ContentView .

علاوه بر این، کلاس های زیر پیاده سازی جزئی دارند و تا پایان این Codelab توسط شما تکمیل خواهند شد:

  • ContentView - نمای سطح بالای SwiftUI حاوی برنامه شما.
  • MapViewControllerBridge - کلاسی که نمای UIKit را به نمای SwiftUI پیوند می دهد. به طور خاص، این کلاسی است که MapViewController را در MapViewController قابل دسترسی می کند.

5. استفاده از SwiftUI در مقابل UIKit

SwiftUI در iOS 13 به عنوان یک فریم ورک جایگزین رابط کاربری نسبت به UIKit برای توسعه برنامه های iOS معرفی شد. در مقایسه با UIKit قبلی خود، SwiftUI چندین مزیت را ارائه می دهد. چندتا را نام بردن:

  • هنگام تغییر وضعیت، نمایش ها به طور خودکار به روز می شوند. با استفاده از اشیایی به نام State ، هر تغییری در مقدار اساسی موجود در آن باعث به‌روزرسانی خودکار رابط کاربری می‌شود.
  • پیش نمایش زنده توسعه سریعتر را امکان پذیر می کند. پیش‌نمایش‌های زنده نیاز به ساخت و استقرار کد در شبیه‌ساز را برای مشاهده تغییرات بصری به حداقل می‌رسانند، زیرا پیش‌نمایش نمای SwiftUI به راحتی در Xcode قابل مشاهده است.
  • منبع حقیقت در سوئیفت است. همه نماها در SwiftUI در سوئیفت اعلان می شوند، بنابراین استفاده از Interface Builder دیگر ضروری نیست.
  • با UIKit تعامل دارد. قابلیت همکاری با UIKit تضمین می کند که برنامه های موجود می توانند به صورت تدریجی از SwiftUI با نماهای موجود خود استفاده کنند. علاوه بر این، کتابخانه‌هایی که هنوز از SwiftUI پشتیبانی نمی‌کنند، مانند Maps SDK برای iOS، همچنان می‌توانند در SwiftUI استفاده شوند.

برخی از اشکالات نیز وجود دارد:

  • SwiftUI فقط در iOS 13 یا بالاتر در دسترس است.
  • سلسله مراتب نمایش را نمی توان در پیش نمایش های Xcode بررسی کرد.

وضعیت SwiftUI و جریان داده

SwiftUI یک روش جدید برای ایجاد رابط کاربری با استفاده از یک رویکرد اعلانی ارائه می‌دهد—شما به SwiftUI می‌گویید که می‌خواهید نمای شما همراه با تمام حالت‌های مختلف برای آن چگونه به نظر برسد، و سیستم بقیه کارها را انجام خواهد داد. SwiftUI هر زمان که وضعیت اساسی به دلیل یک رویداد یا اقدام کاربر تغییر کند، نمای را به روز می کند. این طرح معمولاً به جریان داده های یک طرفه گفته می شود. در حالی که ویژگی‌های این طراحی در این نرم‌افزار کد خارج از محدوده است، توصیه می‌کنیم نحوه عملکرد آن را در اسناد وضعیت و جریان داده اپل مطالعه کنید.

پل زدن UIKit و SwiftUI با استفاده از UIViewRepresentable یا UIViewControllerRepresentable

از آنجایی که Maps SDK برای iOS بر روی UIKit ساخته شده است و هنوز نمای سازگار با SwiftUI ارائه نمی دهد، استفاده از آن در SwiftUI مستلزم انطباق با UIViewRepresentable یا UIViewControllerRepresentable است. این پروتکل‌ها SwiftUI را قادر می‌سازد تا به UIView و UIViewController ساخته‌شده توسط UIKit را شامل شود. در حالی که می‌توانید از هر یک از پروتکل‌ها برای افزودن نقشه Google به نمای SwiftUI استفاده کنید، در مرحله بعد، نگاهی به استفاده از UIViewControllerRepresentable برای گنجاندن یک UIViewController حاوی نقشه خواهیم داشت.

6. یک نقشه اضافه کنید

در این بخش، نقشه های گوگل را به نمای SwiftUI اضافه می کنید.

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

کلید API خود را اضافه کنید

کلید API که در مرحله قبل ایجاد کردید باید به Maps SDK برای iOS ارائه شود تا حساب شما را با نقشه ای که در برنامه نمایش داده می شود مرتبط کند.

برای ارائه کلید API خود، فایل AppDelegate.swift را باز کنید و به روش application(_, didFinishLaunchingWithOptions) . در حال حاضر، SDK از طریق GMSServices.provideAPIKey() با رشته "YOUR_API_KEY" مقداردهی اولیه می شود. آن رشته را با کلید API خود جایگزین کنید. با تکمیل این مرحله، Maps SDK برای iOS هنگام راه‌اندازی برنامه، مقداردهی اولیه می‌شود.

با استفاده از MapViewControllerBridge یک نقشه گوگل اضافه کنید

اکنون که کلید API شما در حال ارائه به SDK است، مرحله بعدی نمایش نقشه در برنامه است.

کنترلر View که در کد شروع ارائه شده است، MapViewController در حال حاضر حاوی یک GMSMapView در نمای خود است. با این حال، از آنجایی که این view controller در UIKit ایجاد شده است، باید این کلاس را به SwiftUI متصل کنید تا بتوان از آن در ContentView استفاده کرد. برای انجام این کار:

  1. فایل MapViewControllerBridge را در Xcode باز کنید.

این کلاس با 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) {
  }
}

از MapViewControllerBridge در ContentView استفاده کنید

اکنون که MapViewControllerBridge در حال ایجاد یک نمونه از MapViewController است، قدم بعدی استفاده از این ساختار در ContentView برای نمایش نقشه است.

  1. فایل ContentView را در Xcode باز کنید.

ContentView در SceneDelegate نمونه سازی شده است و شامل نمای برنامه سطح بالا است. نقشه از داخل این فایل اضافه خواهد شد.

  1. یک 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()
      } // ...
    }
  }
}
  1. حالا ادامه دهید و برنامه را اجرا کنید. اکنون باید بارگذاری نقشه را روی صفحه دستگاه خود به همراه فهرستی از شهرها به سمت پایین صفحه مشاهده کنید.

7. نشانگرها را به نقشه اضافه کنید

در مرحله قبل، یک نقشه را در کنار یک لیست قابل تعامل که لیستی از شهرها را نمایش می دهد، اضافه کردید. در این بخش، نشانگرهایی را برای هر شهر در آن لیست اضافه خواهید کرد.

map-with-markers@2x.png

نشانگرها به عنوان حالت

ContentView در حال حاضر یک ویژگی به نام markers را اعلام می کند که لیستی از GMSMarker است که نشان دهنده هر شهر اعلام شده در ویژگی ساکن cities است. توجه داشته باشید که این ویژگی با وضعیت بسته بندی ویژگی 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 استفاده می کند.

لیست شهرها

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 منتقل کنید

علاوه بر لیست شهرهایی که داده ها را از ویژگی markers نمایش می دهند، این ویژگی را به ساختار MapViewControllerBridge کنید تا بتوان از آن برای نمایش آن نشانگرها روی نقشه استفاده کرد. برای انجام آن:

  1. یک ویژگی markers جدید را در MapViewControllerBridge که با @Binding حاشیه نویسی شده است، اعلام کنید

MapViewControllerBridge

struct MapViewControllerBridge: : UIViewControllerRepresentable {
  @Binding var markers: [GMSMarker]
  // ...
}
  1. در 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 }
  }
}
  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. به یک شهر انتخاب شده متحرک شوید

در مرحله قبل، با انتقال State از یک نمای SwiftUI به نمای دیگر، نشانگرها را به نقشه اضافه کردید. در این مرحله، پس از اینکه یک شهر/نشانگر در لیست تعاملی قرار گرفت، متحرک سازی خواهید کرد. برای اجرای انیمیشن، با تغییر موقعیت دوربین نقشه هنگام تغییر، به تغییرات یک وضعیت واکنش نشان خواهید داد. برای کسب اطلاعات بیشتر در مورد مفهوم دوربین نقشه، به دوربین و مشاهده مراجعه کنید.

animate-city@2x.png

متحرک کردن نقشه به شهر انتخاب شده

برای متحرک سازی نقشه به یک شهر انتخاب شده:

  1. یک Binding جدید در MapViewControllerBridge تعریف کنید

ContentView دارای یک ویژگی State selectedMarker نام SelectMarker است که مقدار اولیه آن صفر می شود و هر زمان که شهری در لیست انتخاب شود به روز می شود. این کار توسط CitiesList view buttonAction در ContentView انجام می شود.

ContentView

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

هر زمان selectedMarker SelectMarker تغییر کند، MapViewControllerBridge باید از این تغییر حالت آگاه باشد تا بتواند نقشه را به نشانگر انتخاب شده متحرک کند. بنابراین، یک Binding جدید در MapViewControllerBridge از نوع GMSMarker و نام ویژگی را selectedMarker بگذارید.

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  @Binding var selectedMarker: GMSMarker?
}
  1. MapViewControllerBridge را به‌روزرسانی کنید تا هر زمان که علامت‌گذاری selectedMarker تغییر کند، نقشه را متحرک کنید

هنگامی که یک Binding جدید اعلام شد، باید 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) دنباله ای از انیمیشن های نقشه را با استفاده از عملکرد animate(with) GMSMapView انجام می دهد.

  1. selectedMarker نشانگر ContentView را به MapViewControllerBridge

هنگامی که MapViewControllerBridge Binding جدید را اعلام کرد، ادامه دهید و ContentView را به روز کنید تا در MapViewControllerBridge selectedMarker آن نصب شده است عبور کنید.

ContentView

struct ContentView: View {
  // ...
  var body: some View {
    // ...
    GeometryReader { geometry in
      ZStack(alignment: .top) {
        // Map
        MapViewControllerBridge(markers: $markers, selectedMarker: $selectedMarker)
        // ...
      }
    }
  }
}

با تکمیل این مرحله، هر زمان که شهر جدیدی در لیست انتخاب شود، نقشه متحرک می شود.

نمای SwiftUI را متحرک کنید تا بر شهر تأکید کنید

SwiftUI متحرک سازی نماها را بسیار آسان می کند زیرا اجرای انیمیشن ها را برای انتقال وضعیت مدیریت می کند. برای نشان دادن این موضوع، پس از تکمیل انیمیشن نقشه، با متمرکز کردن نمای روی شهر انتخابی، انیمیشن های بیشتری اضافه می کنید. برای انجام این کار مراحل زیر را انجام دهید:

  1. یک بسته 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()
            })
          })
        }
      }
    }
  }
}
  1. پیاده سازی 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))
      }
    }
  }
}
  1. ادامه دهید و برنامه را اجرا کنید تا انیمیشن ها را ببینید!

9. یک رویداد به SwiftUI ارسال کنید

در این مرحله، شما به رویدادهای منتشر شده از GMSMapView گوش می دهید و آن رویداد را به SwiftUI ارسال می کنید. به طور خاص، شما یک نماینده را برای نمای نقشه تنظیم می‌کنید و به رویدادهای حرکت دوربین گوش می‌دهید، به طوری که وقتی یک شهر متمرکز است و دوربین نقشه از یک حرکت حرکت می‌کند، نمای نقشه از حالت فوکوس خارج می‌شود تا بتوانید نقشه بیشتری را ببینید.

با استفاده از هماهنگ کننده های SwiftUI

GMSMapView رویدادهایی مانند تغییر موقعیت دوربین یا زمانی که یک نشانگر ضربه می‌خورد را منتشر می‌کند. مکانیسم گوش دادن به این رویدادها از طریق پروتکل GMSMapViewDelegate است. SwiftUI مفهوم یک Coordinator را معرفی می کند که به طور خاص برای عمل به عنوان نماینده برای کنترل کننده های view UIKit استفاده می شود. بنابراین، در دنیای SwiftUI، یک Coordinator باید مسئول انطباق با پروتکل GMSMapViewDelegate باشد. برای این کار مراحل زیر را انجام دهید:

  1. یک Coordinator به نام MapViewCoordinator در MapViewControllerBridge

یک کلاس تودرتو در داخل کلاس MapViewControllerBridge و آن را MapViewCoordinator . این کلاس باید با GMSMapViewDelegate مطابقت داشته باشد و MapViewControllerBridge را به عنوان یک ویژگی اعلام کند.

MapViewControllerBridge

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

    init(_ mapViewControllerBridge: MapViewControllerBridge) {
      self.mapViewControllerBridge = mapViewControllerBridge
    }
  }
}
  1. makeCoordinator() makeCoordinator را در MapViewControllerBridge پیاده سازی کنید

سپس، makeCoordinator() را در MapViewControllerBridge پیاده سازی کنید و نمونه ای از MapViewCoodinator را که در مرحله قبل ایجاد کردید، برگردانید.

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  func makeCoordinator() -> MapViewCoordinator {
    return MapViewCoordinator(self)
  }
}
  1. MapViewCoordinator را به عنوان نماینده نمای نقشه تنظیم کنید

با ایجاد هماهنگ کننده سفارشی، گام بعدی این است که هماهنگ کننده را به عنوان نماینده برای نمای نقشه view controller تنظیم کنید. برای انجام این کار، مقداردهی اولیه view controller را در makeUIViewController(context) به روز کنید. هماهنگ کننده ایجاد شده از مرحله قبل از شی 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. 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
        })
        // ...
      }
    }
  }
}
  1. ادامه دهید و برنامه را اجرا کنید تا تغییرات جدید را ببینید!

10. تبریک می گویم

تبریک می گویم برای رسیدن به اینجا! شما زمینه های زیادی را پوشش دادید و امیدواریم درسی که آموخته اید به شما این امکان را می دهد که اکنون برنامه SwiftUI خود را با استفاده از Maps SDK برای iOS بسازید.

چیزی که یاد گرفتی

  • تفاوت بین SwiftUI و UIKit
  • نحوه پل زدن بین SwiftUI و UIKit با استفاده از UIViewControllerRepresentable
  • نحوه ایجاد تغییرات در نمای نقشه با State و Binding
  • نحوه ارسال یک رویداد از نمای نقشه به SwiftUI با استفاده از یک Coordinator

بعدش چی؟

  • Maps SDK برای iOS - اسناد رسمی برای Maps SDK برای iOS
  • مکان‌ها SDK برای iOS - کسب‌وکارهای محلی و نقاط مورد علاقه را در اطراف خود پیدا کنید
  • maps-sdk-for-ios-samples - کد نمونه در GitHub که تمام ویژگی‌های موجود در Maps SDK برای iOS را نشان می‌دهد.
  • SwiftUI - اسناد رسمی اپل در مورد SwiftUI
  • با پاسخ به سوال زیر به ما کمک کنید محتوایی را ایجاد کنیم که برای شما مفیدتر باشد:

دوست دارید چه کدهای دیگری را ببینید؟

تجسم داده ها بر روی نقشه ها بیشتر در مورد سفارشی کردن سبک نقشه های من ساختمان برای تعاملات سه بعدی در نقشه ها

آیا کد لبه مورد نظر شما در بالا فهرست نشده است؟ آن را با یک شماره جدید در اینجا درخواست کنید .