اولین نقشه سه بعدی خود را با SwiftUI بسازید

1. قبل از شروع

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

برنامه ای که نقشه سه بعدی سانفرانسیسکو را نشان می دهد

یاد خواهید گرفت:

  • نحوه کنترل دوربین برای مشاهده مکان ها و پرواز در اطراف نقشه.
  • نحوه اضافه کردن نشانگرها و مدل ها
  • نحوه رسم خطوط و چند ضلعی ها
  • نحوه مدیریت کلیک های کاربر روی نشانگرهای مکان.

پیش نیازها

  • یک پروژه Google Console با فعال کردن صورت‌حساب
  • یک کلید API، که به صورت اختیاری محدود به Maps 3D SDK برای iOS است.
  • دانش اولیه توسعه iOS با SwiftUI.

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

  • Xcode را تنظیم کنید و SDK را با استفاده از Swift Package Manager وارد کنید
  • برنامه خود را برای استفاده از کلید API پیکربندی کنید
  • یک نقشه سه بعدی اولیه را به برنامه خود اضافه کنید
  • دوربین را برای پرواز به مکان های خاص و اطراف آن کنترل کنید
  • نشانگرها، خطوط، چند ضلعی ها و مدل ها را به نقشه خود اضافه کنید

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

  • Xcode 15 یا بالاتر.

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

برای مرحله فعال سازی زیر، باید Maps 3D 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 Maps به یک کلید API نیاز دارند.

Maps 3D SDK را برای iOS فعال کنید

می‌توانید Maps 3D SDK برای iOS را با استفاده از پیوند منوی Google Maps Platform > APIs and Services در کنسول پیدا کنید.

روی Enable کلیک کنید تا API در پروژه انتخابی شما فعال شود.

Maps 3D SDK را در Google Console فعال کنید

3. یک برنامه اصلی SwiftUI ایجاد کنید

توجه: می‌توانید کد راه‌حل را برای هر مرحله در مخزن نمونه برنامه Codelab در GitHub پیدا کنید.

یک برنامه جدید در Xcode ایجاد کنید.

کد این مرحله را می‌توانید در پوشه GoogleMaps3DDemo در GitHub پیدا کنید.

Xcode را باز کنید و یک برنامه جدید ایجاد کنید. SwiftUI را مشخص کنید.

با برنامه GoogleMaps3DDemo با نام بسته com.example.GoogleMaps3DDemo تماس بگیرید.

کتابخانه GoogleMaps3D را به پروژه خود وارد کنید

با استفاده از Swift Package Manager، SDK را به پروژه خود اضافه کنید.

در پروژه یا فضای کاری Xcode خود، به File > Add Package Dependencies بروید. https://github.com/googlemaps/ios-maps-3d-sdk را به عنوان URL وارد کنید، Enter را فشار دهید تا بسته را وارد کنید و روی «افزودن بسته» کلیک کنید.

از پنجره انتخاب محصولات بسته، بررسی کنید که GoogleMaps3D به هدف اصلی تعیین شده شما اضافه شود. پس از تکمیل، روی Add Package کلیک کنید.

برای تأیید نصب خود، به صفحه عمومی هدف خود بروید. در Frameworks، Libraries و Embedded Content، باید بسته های نصب شده را ببینید. همچنین می‌توانید بخش Package Dependencies در Project Navigator را مشاهده کنید تا بسته و نسخه آن را تأیید کنید.

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

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

یک فایل کانفیگ جدید در پوشه ریشه پروژه ایجاد کنید

در Xcode، مطمئن شوید که پنجره کاوشگر پروژه را مشاهده می کنید. روی ریشه پروژه کلیک راست کرده و "New File from Template" را انتخاب کنید. پیمایش کنید تا «فایل تنظیمات پیکربندی» را ببینید. این را انتخاب کنید و روی "بعدی" کلیک کنید. نام فایل را Config.xcconfig و مطمئن شوید که پوشه ریشه پروژه انتخاب شده است. برای ایجاد فایل روی "ایجاد" کلیک کنید.

در ویرایشگر، یک خط به فایل پیکربندی به صورت زیر اضافه کنید: MAPS_API_KEY = YOUR_API_KEY

کلید API خود را جایگزین YOUR_API_KEY کنید.

این تنظیم را به Info.plist اضافه کنید.

برای انجام این کار، ریشه پروژه را انتخاب کرده و روی تب "Info" کلیک کنید.

یک ویژگی جدید به نام MAPS_API_KEY با مقدار $(MAPS_API_KEY) اضافه کنید.

کد برنامه نمونه دارای یک فایل Info.plist است که این ویژگی را مشخص می کند.

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

فایلی به نام GoogleMaps3DDemoApp.swift را باز کنید. این نقطه ورودی و مسیریابی اصلی برنامه شما است.

ContentView() را فراخوانی می کند که پیام Hello World را نشان می دهد.

ContentView.swift را در ویرایشگر باز کنید.

یک بیانیه import برای GoogleMaps3D اضافه کنید.

کد داخل var body: some View {} . یک Map() جدید در داخل body اعلام کنید.

حداقل پیکربندی که برای مقداردهی اولیه Map نیاز دارید، MapMode است. این دو مقدار ممکن دارد:

  • .hybrid - تصاویر ماهواره ای با جاده ها و برچسب ها، یا
  • .satellite - فقط تصاویر ماهواره ای.

.hybrid را انتخاب کنید.

فایل ContentView.swift شما باید شبیه این باشد.

import GoogleMaps3D
import SwiftUI

@main
struct ContentView: View {
    var body: some View {
      Map(mode: .hybrid)
    }
}

کلید API خود را تنظیم کنید.

کلید API باید قبل از مقداردهی اولیه نقشه تنظیم شود.

می توانید این کار را با تنظیم Map.apiKey در کنترل کننده رویداد init() هر View که حاوی نقشه است انجام دهید. همچنین می توانید قبل از فراخوانی ContentView() آن را در GoogleMaps3DDemoApp.swift تنظیم کنید.

در GoogleMaps3DDemoApp.swift ، Map.apiKey در کنترل کننده رویداد onAppear WindowGroup تنظیم کنید.

کلید API خود را از فایل پیکربندی واکشی کنید

از Bundle.main.infoDictionary برای دسترسی به تنظیمات MAPS_API_KEY که در فایل پیکربندی خود ایجاد کرده اید، استفاده کنید.

import GoogleMaps3D
import SwiftUI

@main
struct GoogleMaps3DDemoApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
    }
    .onAppear {
      guard let infoDictionary: [String: Any] = Bundle.main.infoDictionary else {
        fatalError("Info.plist not found")
      }
      guard let apiKey: String = infoDictionary["MAPS_API_KEY"] as? String else {
        fatalError("MAPS_API_KEY not set in Info.plist")
      }
      Map.apiKey = apiKey
    }
  }
}

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

نقشه سه بعدی زمین را نشان می دهد

4. از دوربین برای کنترل نمای نقشه استفاده کنید

یک شی وضعیت دوربین ایجاد کنید

نماهای نقشه سه بعدی توسط کلاس Camera کنترل می شود. در این مرحله یاد خواهید گرفت که مکان، ارتفاع، عنوان، شیب، رول و محدوده را برای سفارشی کردن نمای نقشه مشخص کنید.

نمای نقشه سه بعدی سانفرانسیسکو

یک کلاس Helpers برای ذخیره تنظیمات دوربین ایجاد کنید

یک فایل خالی جدید به نام MapHelpers.swift اضافه کنید. در فایل جدید خود، GoogleMaps3D را وارد کنید و یک برنامه افزودنی به کلاس Camera اضافه کنید. یک متغیر به نام sanFrancisco اضافه کنید. این متغیر را به عنوان یک شیء Camera جدید راه اندازی کنید. دوربین را در latitude: 37.39, longitude: -122.08 قرار دهید.

import GoogleMaps3D

extension Camera {
 public static var sanFrancisco: Camera = .init(latitude: 37.39, longitude: -122.08)
}

یک نمای جدید به برنامه خود اضافه کنید

یک فایل جدید به نام CameraDemo.swift ایجاد کنید. طرح کلی یک نمای SwiftUI جدید را به فایل اضافه کنید.

یک متغیر @State به نام camera از نوع Camera اضافه کنید. آن را در دوربین sanFrancisco که به تازگی تعریف کردید، مقداردهی اولیه کنید.

استفاده از @State به شما امکان می دهد نقشه را به وضعیت دوربین متصل کنید و از آن به عنوان منبع حقیقت استفاده کنید.

@State var camera: Camera = .sanFrancisco

فراخوانی تابع Map() را تغییر دهید تا دارای ویژگی camera باشد. برای مقداردهی اولیه ویژگی camera به شئ Camera @State ( .sanFrancisco ) از حالت camera binding $camera استفاده کنید.

import SwiftUI
import GoogleMaps3D

struct CameraDemo: View {
  @State var camera: Camera = .sanFrancisco
  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid)
    }
  }
}

رابط کاربری اصلی ناوبری را به برنامه خود اضافه کنید

یک NavigationView به نقطه ورودی اصلی برنامه، GoogleMaps3DDemoApp.swift اضافه کنید.

این به کاربران امکان می دهد لیستی از دموها را ببینند و روی هر کدام کلیک کنند تا باز شود.

برای افزودن NavigationView جدید، GoogleMaps3DDemoApp.swift را ویرایش کنید.

یک List حاوی دو اعلان NavigationLink اضافه کنید.

اولین NavigationLink باید ContentView() با توضیحات Text Basic Map باز کند.

دومین NavigationLink باید CameraDemo() باز کند.

...
      NavigationView {
        List {
          NavigationLink(destination: ContentView()) {
            Text("Basic Map")
          }
          NavigationLink(destination: CameraDemo()) {
            Text("Camera Demo")
          }
        }
      }
...

یک پیش نمایش Xcode اضافه کنید

پیش نمایش ها یک ویژگی قدرتمند Xcode است که به شما امکان می دهد برنامه خود را هنگام ایجاد تغییرات در آن ببینید و با آن تعامل داشته باشید.

برای افزودن پیش‌نمایش، CameraDemo.swift را باز کنید. یک بلوک کد #Preview {} خارج از struct اضافه کنید.

#Preview {
 CameraDemo()
}

پنجره پیش نمایش را در Xcode باز یا تازه کنید. نقشه باید سانفرانسیسکو را نشان دهد.

نماهای سه بعدی سفارشی را تنظیم کنید

می توانید پارامترهای اضافی را برای کنترل دوربین مشخص کنید:

  • heading : یاتاقان بر حسب درجه از شمال به سمت دوربین.
  • tilt : زاویه شیب بر حسب درجه، که 0 مستقیماً بالای سر و 90 به صورت افقی است.
  • roll : زاویه چرخش در اطراف صفحه عمودی دوربین، بر حسب درجه
  • range : فاصله دوربین بر حسب متر از موقعیت جغرافیایی، طول جغرافیایی
  • altitude : ارتفاع دوربین از سطح دریا

اگر هیچ یک از این پارامترهای اضافی را ارائه نکنید، از مقادیر پیش فرض استفاده می شود.

برای اینکه نمای دوربین داده‌های سه بعدی بیشتری را نشان دهد، پارامترهای اولیه را طوری تنظیم کنید که نمای نزدیک‌تر و کج‌تری را نشان دهد.

Camera که در MapHelpers.swift تعریف کرده‌اید ویرایش کنید تا مقادیر altitude ، heading ، tilt ، roll و range را شامل شود.

public static var sanFrancisco: Camera = .init(
  latitude: 37.7845812,
  longitude: -122.3660241,
  altitude: 585,
  heading: 288.0,
  tilt: 75.0,
  roll: 0.0,
  range: 100)

برنامه را بسازید و اجرا کنید تا نمای سه بعدی جدید را ببینید و کاوش کنید.

5. انیمیشن های دوربین اصلی

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

نقشه سه بعدی سیاتل

به یک مکان پرواز کنید

شما از متد Map.flyCameraTo() برای متحرک سازی دوربین از محل اولیه به مکان جدید استفاده خواهید کرد.

متد flyCameraTo() تعدادی پارامتر را می گیرد:

  • Camera که مکان پایانی را نشان می دهد.
  • duration : مدت زمان اجرای انیمیشن، در چند ثانیه.
  • trigger : یک شی قابل مشاهده که انیمیشن را با تغییر حالت آن فعال می کند.
  • completion : کدی که پس از تکمیل انیمیشن اجرا می شود.

مکانی را برای پرواز تعیین کنید

فایل MapHelpers.swift خود را باز کنید.

یک شی دوربین جدید برای نشان دادن سیاتل تعریف کنید.

public static var seattle: Camera = .init(latitude:
47.6210296,longitude: -122.3496903, heading: 149.0, tilt: 77.0, roll: 0.0, range: 4000)

یک دکمه برای فعال کردن انیمیشن اضافه کنید.

CameraDemo.swift خود را باز کنید. یک متغیر بولی جدید در داخل struct اعلان کنید.

آن را animate با مقدار اولیه false بنامید.

@State private var animate: Bool = false

یک Button زیر VStack اضافه کنید. Button انیمیشن نقشه را آغاز می کند.

به Button Text مناسبی مانند "شروع پرواز" بدهید.

import SwiftUI
import GoogleMaps3D

struct CameraDemo: View {
  @State var camera:Camera = .sanFrancisco
  @State private var animate: Bool = false

  var body: some View {
    VStack{
      Map(camera: $camera, mode: .hybrid)
      Button("Start Flying") {
      }
    }
  }
}

در بسته شدن دکمه، کدی را برای تغییر وضعیت متغیر animate اضافه کنید.

      Button("Start Flying") {
        animate.toggle()
      }

انیمیشن را شروع کنید.

هنگامی که وضعیت متغیر animate تغییر می کند، کدی را برای فعال کردن انیمیشن flyCameraTo() اضافه کنید.

  var body: some View {
    VStack{
      Map(camera: $camera, mode: .hybrid)
        .flyCameraTo(
          .seattle,
          duration: 5,
          trigger: animate,
          completion: {  }
        )
      Button("Start Flying") {
        animate.toggle()
      }
    }
  }

در اطراف یک مکان پرواز کنید

با استفاده از روش Map.flyCameraAround() می توان به پرواز در اطراف یک مکان دست یافت. این روش چندین پارامتر دارد:

  • یک Camera که مکان و نمای را مشخص می کند.
  • duration در ثانیه
  • rounds : تعداد دفعات تکرار انیمیشن.
  • trigger : یک شی قابل مشاهده که انیمیشن را فعال می کند.
  • callback : کدی که با اجرای انیمیشن اجرا می شود.

یک متغیر @State به نام flyAround با مقدار اولیه false تعریف کنید.

هنگامی که این کار را انجام دادید، بلافاصله پس از فراخوانی متد flyCameraTo() flyCameraAround() اضافه کنید.

مدت زمان پرواز در اطراف باید نسبتا طولانی باشد تا دید به آرامی تغییر کند.

مطمئن شوید که انیمیشن flyCameraAround() را با تغییر وضعیت شی ماشه پس از تکمیل flyCameraTo() فعال کنید.

کد شما باید شبیه این باشد.

import SwiftUI
import GoogleMaps3D

struct CameraDemo: View {
  @State var camera:Camera = .sanFrancisco
  @State private var animate: Bool = false
  @State private var flyAround: Bool = false

  var body: some View {
    VStack{
      Map(camera: $camera, mode: .hybrid)
        .flyCameraTo(
          .seattle,
          duration: 5,
          trigger: animate,
          completion: { flyAround = true }
        )
        .flyCameraAround(
          .seattle,
          duration: 15,
          rounds: 0.5,
          trigger: flyAround,
          callback: {  }
        )
      Button("Start Flying") {
        animate.toggle()
      }
    }
  }
}

#Preview {
  CameraDemo()
}

برنامه را پیش نمایش یا اجرا کنید تا ببینید که دوربین پس از تکمیل انیمیشن flyCameraTo() در اطراف مقصد پرواز می کند.

6. یک نشانگر به نقشه خود اضافه کنید.

در این مرحله یاد خواهید گرفت که چگونه یک پین نشانگر روی نقشه بکشید.

شما یک شی Marker ایجاد می کنید و آن را به نقشه خود اضافه می کنید. SDK از یک نماد پیش‌فرض برای نشانگر استفاده می‌کند. در نهایت ارتفاع نشانگر و سایر خصوصیات را برای تغییر نحوه نمایش آن تنظیم خواهید کرد.

نقشه سه بعدی نشانگر نقشه را نشان می دهد

یک نمای SwiftUI جدید برای نمایشگر نشانگر خود ایجاد کنید.

یک فایل Swift جدید به پروژه خود اضافه کنید. نام آن را MarkerDemo.swift بگذارید.

طرح کلی یک نمای SwiftUI را اضافه کنید و نقشه را همانطور که در CameraDemo انجام دادید مقداردهی اولیه کنید.

import SwiftUI
import GoogleMaps3D

struct MarkerDemo: View {
  @State var camera: Camera = .sanFrancisco
  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid)
    }
  }
}

یک شی نشانگر را مقدار دهی اولیه کنید

یک متغیر نشانگر جدید به نام mapMarker اعلام کنید. در بالای بلوک کد struct در MarkerDemo.swift .

تعریف را در خط زیر اعلامیه camera خود قرار دهید. این کد نمونه تمام ویژگی های موجود را مقداردهی اولیه می کند.

  @State var mapMarker: Marker = .init(
    position: .init(
      latitude: 37.8044862,
      longitude: -122.4301493,
      altitude: 0.0),
    altitudeMode: .absolute,
    collisionBehavior: .required,
    extruded: false,
    drawsWhenOccluded: true,
    sizePreserved: true,
    zIndex: 0,
    label: "Test"
  )

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

برای ترسیم نشانگر، آن را به یک بسته که هنگام ایجاد نقشه نامیده می شود، اضافه کنید.

struct MarkerDemo: View {
  @State var camera: Camera = .sanFrancisco
  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {
        mapMarker
      }
    }
  }
}

یک NavigationLink جدید به GoogleMaps3DDemoApp.swift با مقصد MarkerDemo() و Text که آن را به عنوان "Marker Demo" توصیف می کند، اضافه کنید.

...
      NavigationView {
        List {
          NavigationLink(destination: Map()) {
            Text("Basic Map")
          }
          NavigationLink(destination: CameraDemo()) {
            Text("Camera Demo")
          }
          NavigationLink(destination: MarkerDemo()) {
            Text("Marker Demo")
          }
        }
      }
...

برنامه خود را پیش نمایش و اجرا کنید

پیش نمایش را بازخوانی کنید یا برنامه خود را اجرا کنید تا نشانگر را ببینید.

نشانگرهای اکسترود شده

نشانگرها را می توان با استفاده از altitude و altitudeMode در بالای سطح زمین یا شبکه سه بعدی قرار داد.

اعلان mapMarker را در MarkerDemo.swift در یک متغیر Marker جدید به نام extrudedMarker کپی کنید.

یک مقدار غیر صفر برای altitude تنظیم کنید، 50 کافی است.

altitudeMode به .relativeToMesh تغییر دهید و extruded روی true قرار دهید. از latitude و longitude از قطعه کد در اینجا برای قرار دادن نشانگر در بالای یک آسمان خراش استفاده کنید.

  @State var extrudedMarker: Marker = .init(
    position: .init(
      latitude: 37.78980534,
      longitude:  -122.3969349,
      altitude: 50.0),
    altitudeMode: .relativeToMesh,
    collisionBehavior: .required,
    extruded: true,
    drawsWhenOccluded: true,
    sizePreserved: true,
    zIndex: 0,
    label: "Extruded"
  )

دوباره برنامه را اجرا یا پیش نمایش کنید. نشانگر باید در بالای یک ساختمان سه بعدی ظاهر شود.

7. یک مدل به نقشه خود اضافه کنید.

یک Model می توان به همان روشی که یک Marker اضافه کرد. شما به یک فایل مدل نیاز دارید که می توان از طریق URL به آن دسترسی پیدا کرد یا به عنوان یک فایل محلی در پروژه خود اضافه کرد. برای این مرحله از یک فایل محلی استفاده می کنیم که می توانید آن را از مخزن GitHub برای این کد لبه دانلود کنید.

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

یک فایل مدل به پروژه خود اضافه کنید

یک پوشه جدید در پروژه Xcode خود به نام Models ایجاد کنید.

مدل را از مخزن نمونه برنامه GitHub دانلود کنید. با کشیدن آن به پوشه جدید در نمای پروژه Xcode، آن را به پروژه خود اضافه کنید.

مطمئن شوید که هدف را هدف اصلی برنامه خود قرار داده اید.

تنظیمات Build Phases > Copy Bundle Resources را برای پروژه خود بررسی کنید. فایل مدل باید در لیست منابع کپی شده در بسته باشد. اگر آنجا نیست روی "+" کلیک کنید تا آن را اضافه کنید.

مدل را به برنامه خود اضافه کنید.

یک فایل SwiftUI جدید به نام ModelDemo.swift ایجاد کنید.

مانند مراحل قبل، عبارت های import را برای SwiftUI و GoogleMaps3D اضافه کنید.

یک Map در داخل VStack در body خود اعلام کنید.

import SwiftUI
import GoogleMaps3D

struct ModelDemo: View {
  @State var camera: Camera = .sanFrancisco
  
  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {
        
      }
    }
  }
}

مسیر مدل را از Bundle خود دریافت کنید. کد این مورد را خارج از struct اضافه کنید.

private let fileUrl = Bundle.main.url(forResource: "balloon", withExtension: "glb")

یک متغیر برای مدل خود در داخل ساختار اعلان کنید.

یک مقدار پیش‌فرض در case fileUrl ارائه نمی‌شود.

  @State var balloonModel: Model = .init(
    position: .init(
      latitude: 37.791376,
      longitude: -122.397571,
      altitude: 300.0),
    url: URL(fileURLWithPath: fileUrl?.relativePath ?? ""),
    altitudeMode: .absolute,
    scale: .init(x: 5, y: 5, z: 5),
    orientation: .init(heading: 0, tilt: 0, roll: 0)
  )

3. از مدل با نقشه خود استفاده کنید.

همانند افزودن Marker ، فقط مرجع Model خود را در اعلان Map ارائه کنید.

  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {
        balloonModel
      }
    }
  }

برنامه خود را پیش نمایش و اجرا کنید

یک NavigationLink جدید به GoogleMaps3DDemoApp.swift ، با مقصد ModelDemo() و Text "Model Demo" اضافه کنید.

...
          NavigationLink(destination: ModelDemo()) {
            Text("Model Demo")
          }
...

پیش نمایش را بازخوانی کنید یا برنامه خود را اجرا کنید تا مدل را ببینید.

8. روی نقشه خود یک خط و یک چند ضلعی بکشید.

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

برای سادگی، اشکال را به عنوان آرایه هایی از اشیاء LatLngAltitude تعریف می کنید. در یک برنامه واقعی، داده ها ممکن است از یک فایل، یک فراخوانی API یا یک پایگاه داده بارگیری شوند.

نقشه سه بعدی سانفرانسیسکو که دو چند ضلعی و یک چند خط را نشان می دهد

برای مدیریت داده های شکل، چند شیء شکل ایجاد کنید.

یک تعریف Camera جدید به MapHelpers.swift اضافه کنید که به مرکز شهر سانفرانسیسکو می‌نگرد.

  public static var downtownSanFrancisco: Camera = .init(latitude: 37.7905, longitude: -122.3989, heading: 25, tilt: 71, range: 2500) 

یک فایل جدید به نام ShapesDemo.swift به پروژه خود اضافه کنید. struct به نام ShapesDemo اضافه کنید که پروتکل View را پیاده سازی می کند و body ای به آن اضافه کنید.

struct ShapesDemo: View {
  @State var camera: Camera = .downtownSanFrancisco

  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {

      }
    }
  }
}

کلاس هایی که برای مدیریت داده های شکل استفاده خواهید کرد Polyline و Polygon هستند. ShapesDemo.swift باز کرده و به شکل زیر به struct اضافه کنید.

var polyline: Polyline = .init(coordinates: [
    LatLngAltitude(latitude: 37.80515638571346, longitude: -122.4032569467164, altitude: 0),
    LatLngAltitude(latitude: 37.80337073509504, longitude: -122.4012878349353, altitude: 0),
    LatLngAltitude(latitude: 37.79925208843463, longitude: -122.3976697250461, altitude: 0),
    LatLngAltitude(latitude: 37.7989102378512, longitude: -122.3983408725656, altitude: 0),
    LatLngAltitude(latitude: 37.79887832784348, longitude: -122.3987094864192, altitude: 0),
    LatLngAltitude(latitude: 37.79786443410338, longitude: -122.4066878788802, altitude: 0),
    LatLngAltitude(latitude: 37.79549248916587, longitude: -122.4032992702785, altitude: 0),
    LatLngAltitude(latitude: 37.78861484290265, longitude: -122.4019489189814, altitude: 0),
    LatLngAltitude(latitude: 37.78618687561075, longitude: -122.398969592545, altitude: 0),
    LatLngAltitude(latitude: 37.7892310309145, longitude: -122.3951458683092, altitude: 0),
    LatLngAltitude(latitude: 37.7916358762409, longitude: -122.3981969390652, altitude: 0)
  ])
  .stroke(GoogleMaps3D.Polyline.StrokeStyle(
    strokeColor: UIColor(red: 0.09803921568627451, green: 0.403921568627451, blue: 0.8235294117647058, alpha: 1),
    strokeWidth: 10.0,
    outerColor: .white,
    outerWidth: 0.2
    ))
  .contour(GoogleMaps3D.Polyline.ContourStyle(isGeodesic: true))

  var originPolygon: Polygon = .init(outerCoordinates: [
    LatLngAltitude(latitude: 37.79165766856578, longitude:  -122.3983762901255, altitude: 300),
    LatLngAltitude(latitude: 37.7915324439261, longitude:  -122.3982171091383, altitude: 300),
    LatLngAltitude(latitude: 37.79166617650914, longitude:  -122.3980478493319, altitude: 300),
    LatLngAltitude(latitude: 37.79178986470217, longitude:  -122.3982041104199, altitude: 300),
    LatLngAltitude(latitude: 37.79165766856578, longitude:  -122.3983762901255, altitude: 300 )
  ],
  altitudeMode: .relativeToGround)
  .style(GoogleMaps3D.Polygon.StyleOptions(fillColor:.green, extruded: true) )

  var destinationPolygon: Polygon = .init(outerCoordinates: [
      LatLngAltitude(latitude: 37.80515661739527, longitude:  -122.4034307490334, altitude: 300),
      LatLngAltitude(latitude: 37.80503794515428, longitude:  -122.4032633416024, altitude: 300),
      LatLngAltitude(latitude: 37.80517850164195, longitude:  -122.4031056058006, altitude: 300),
      LatLngAltitude(latitude: 37.80529346901115, longitude:  -122.4032622466595, altitude: 300),
      LatLngAltitude(latitude: 37.80515661739527, longitude:  -122.4034307490334, altitude: 300 )
  ],
  altitudeMode: .relativeToGround)
  .style(GoogleMaps3D.Polygon.StyleOptions(fillColor:.red, extruded: true) )

به پارامترهای اولیه سازی استفاده شده توجه کنید.

  • altitudeMode: .relativeToGround برای اکسترود کردن چند ضلعی ها به ارتفاع خاصی از سطح زمین استفاده می شود.
  • altitudeMode: .clampToGround برای اینکه چند خط از شکل سطح زمین پیروی کند استفاده می شود.
  • استایل ها روی اشیاء Polygon با زنجیر کردن یک فراخوانی متد به styleOptions() پس از فراخوانی .init() تنظیم می شوند.

شکل ها را به نقشه اضافه کنید

درست مانند مراحل قبل، اشکال را می توان مستقیماً به بسته شدن Map اضافه کرد. Map خود را در یک VStack ایجاد کنید.

...
  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid) {
        polyline
        originPolygon
        destinationPolygon
      }
    }
  }
...

برنامه خود را پیش نمایش و اجرا کنید

کد پیش‌نمایش را اضافه کنید و برنامه خود را در قسمت Preview در Xcode بررسی کنید.

#Preview {
  ShapesDemo()
}

برای اجرای برنامه خود، یک NavigationLink جدید به GoogleMaps3DDemoApp.swift اضافه کنید که نمای دمو جدید را باز می کند.

...
          NavigationLink(destination: ShapesDemo()) {
            Text("Shapes Demo")
          }
...

برنامه خود را اجرا کنید و اشکالی را که اضافه کرده اید کاوش کنید.

9. رویدادهای ضربه زدن را روی نشانگرهای مکان مدیریت کنید

در این مرحله یاد می گیرید که چگونه به ضربه های کاربر روی نشانگرهای مکان پاسخ دهید.

نقشه ای که پنجره بازشو با شناسه مکان را نشان می دهد

توجه: برای دیدن نشانگرهای مکان روی نقشه، باید MapMode روی .hybrid تنظیم کنید.

رسیدگی به شیر به پیاده سازی روش Map.onPlaceTap نیاز دارد.

رویداد onPlaceTap یک شی PlaceTapInfo را ارائه می دهد که از آن می توانید شناسه مکان نشانگر Place ضربه خورده را دریافت کنید.

می‌توانید از شناسه مکان برای جستجوی جزئیات بیشتر با استفاده از Places SDK یا Places API استفاده کنید.

یک نمای Swift جدید اضافه کنید

کد زیر را به فایل Swift جدید به نام PlaceTapDemo.swift اضافه کنید.

import GoogleMaps3D
import SwiftUI

struct PlaceTapDemo: View {
  @State var camera: Camera = .sanFrancisco
  @State var isPresented = false
  @State var tapInfo: PlaceTapInfo?

  var body: some View {
    Map(camera: $camera, mode: .hybrid)
      .onPlaceTap { tapInfo in
        self.tapInfo = tapInfo
        isPresented.toggle()
      }
      .alert(
        "Place tapped - \(tapInfo?.placeId ?? "nil")",
        isPresented: $isPresented,
        actions: { Button("OK") {} }
      )
  }
}
#Preview {
  PlaceTapDemo()
}

برنامه خود را پیش نمایش و اجرا کنید

برای پیش نمایش برنامه، صفحه پیش نمایش را باز کنید.

برای اجرای برنامه، یک NavigationLink جدید به GoogleMaps3DDemoApp.swift اضافه کنید.

...
          NavigationLink(destination: PlaceTapDemo()) {
            Text("Place Tap Demo")
          }
...

10. (اختیاری) آن را جلوتر ببرید

انیمیشن های دوربین پیشرفته

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

در این مرحله یاد می گیرید که چگونه لیستی از مکان ها را از یک فایل بارگذاری کنید و هر مکان را به ترتیب متحرک کنید.

نمای نقشه سه بعدی از رویکرد به اینسبروک

فایلی را بارگیری کنید که حاوی یک دنباله مکان است.

flightpath.json از مخزن نمونه برنامه GitHub دانلود کنید.

یک پوشه جدید در پروژه Xcode خود به نام JSON ایجاد کنید.

flightpath.json به پوشه JSON خود در Xcode بکشید.

هدف را به عنوان هدف اصلی برنامه خود تنظیم کنید. بررسی کنید که تنظیمات Copy Bundle Resources پروژه شما شامل این فایل باشد.

دو فایل Swift جدید در برنامه خود به نام‌های FlightPathData.swift و FlightDataLoader.swift ایجاد کنید.

کد زیر را در برنامه خود کپی کنید. این کد ساختارها و کلاس هایی ایجاد می کند که یک فایل محلی به نام "flighpath.json" را می خوانند و آن را به عنوان JSON رمزگشایی می کنند.

ساختارهای FlightPathData و FlightPathLocation ساختار داده را در فایل JSON به عنوان اشیاء Swift نشان می دهند.

کلاس FlightDataLoader داده ها را از فایل می خواند و آن را رمزگشایی می کند. از پروتکل ObservableObject استفاده می کند تا به برنامه شما اجازه دهد تا تغییرات داده های خود را مشاهده کند.

داده های تجزیه شده از طریق یک ویژگی منتشر شده در معرض دید قرار می گیرند.

FlightPaths.swift

import GoogleMaps3D

struct FlightPathData: Decodable {
  let flight: [FlightPathLocation]
}

struct FlightPathLocation: Decodable {
  let timestamp: Int64
  let latitude: Double
  let longitude: Double
  let altitude: Double
  let bearing: Double
  let speed: Double
}

FlightDataLoader.swift

import Foundation

public class FlightDataLoader : ObservableObject {

  @Published var flightPathData: FlightPathData = FlightPathData(flight:[])
  @Published var isLoaded: Bool = false

  public init() {
    load("flightpath.json")
  }
  
  public func load(_ path: String) {
    if let url = Bundle.main.url(forResource: path, withExtension: nil){
      if let data = try? Data(contentsOf: url){
        let jsondecoder = JSONDecoder()
        do{
          let result = try jsondecoder.decode(FlightPathData.self, from: data)
          flightPathData = result
          isLoaded = true
        }
        catch {
          print("Error trying to load or parse the JSON file.")
        }
      }
    }
  }
}

دوربین را در هر مکان متحرک کنید

برای متحرک سازی دوربین بین یک سری مراحل، از KeyframeAnimator استفاده می کنید.

هر Keyframe به عنوان CubicKeyframe ایجاد می شود CubicKeyframe ، بنابراین تغییرات در وضعیت دوربین به آرامی متحرک می شوند.

استفاده از flyCameraTo() باعث می‌شود که نمای بین هر مکان "جهش" پیدا کند.

اول از همه یک دوربین به نام "innsbruck" را در MapHelpers.swift اعلام کنید.

public static var innsbruck: Camera = .init(
  latitude: 47.263,
  longitude: 11.3704,
  altitude: 640.08,
  heading: 237,
  tilt: 80.0,
  roll: 0.0,
  range: 200)

اکنون یک View جدید را در یک فایل جدید به نام FlyAlongRoute.swift تنظیم کنید.

SwiftUI و GoogleMaps3D را وارد کنید. یک Map و یک Button در داخل VStack اضافه کنید. Button را برای تغییر وضعیت متغیر animation Boolean تنظیم کنید.

یک شیء State برای FlightDataLoader اعلان کنید، که فایل JSON را با مقداردهی اولیه بارگذاری می کند.

import GoogleMaps3D
import SwiftUI

struct FlyAlongRoute: View {
  @State private var camera: Camera = .innsbruck
  @State private var flyToDuration: TimeInterval = 5
  @State var animation: Bool = true

  @StateObject var flightData: FlightDataLoader = FlightDataLoader()

  var body: some View {
    VStack {
      Map(camera: $camera, mode: .hybrid)
      Button("Fly Along Route"){
        animation.toggle()
      }
    }
  }
}

فریم های کلیدی را ایجاد کنید

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

دو تابع به ساختار FlyAlongRoute خود اضافه کنید. تابع makeKeyFrame یک CubicKeyframe با حالت دوربین برمی گرداند. تابع makeCamera یک مرحله در توالی داده های پرواز برمی دارد و یک شی Camera را نشان دهنده مرحله برمی گرداند.

func makeKeyFrame(step: FlightPathLocation) -> CubicKeyframe<Camera> {
  return CubicKeyframe(
    makeCamera(step: step),
    duration: flyToDuration
  )
}

func makeCamera(step: FlightPathLocation) -> Camera {
  return .init(
    latitude: step.latitude,
    longitude: step.longitude,
    altitude: step.altitude,
    heading: step.bearing,
    tilt: 75,
    roll: 0,
    range: 200
  )
}

انیمیشن را کنار هم قرار دهید

پس از مقداردهی اولیه Map ، keyframeAnimator فراخوانی کنید و مقادیر اولیه را تنظیم کنید.

شما به حالت اولیه دوربین بر اساس اولین مکان در مسیر پرواز نیاز دارید.

انیمیشن باید بر اساس تغییر حالت متغیر فعال شود.

محتوای keyframeAnimator باید نقشه باشد.

لیست واقعی فریم های کلیدی با حلقه زدن در هر مکان در مسیر پرواز ایجاد می شود.

   VStack {
      Map(camera: $camera, mode: .hybrid)
        .keyframeAnimator(
          initialValue: makeCamera(step: flightData.flightPathData.flight[0]),
          trigger: animation,
          content: { view, value in
            Map(camera: .constant(value), mode: .hybrid)
          },
          keyframes: { _ in
            KeyframeTrack(content: {
              for i in  1...flightData.flightPathData.flight.count-1 {
                makeKeyFrame(step: flightData.flightPathData.flight[i])
              }
            })
          }
        )
   }

برنامه خود را پیش نمایش و اجرا کنید.

برای پیش نمایش نمای خود، صفحه پیش نمایش را باز کنید.

یک NavigationLink جدید با مقصد FlightPathDemo() به GoogleMaps3DDemoApp.swift اضافه کنید و برنامه را اجرا کنید تا آن را امتحان کنید.

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

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

  • یک نقشه سه بعدی اولیه را به برنامه شما اضافه می کند.
  • نشانگرها، خطوط، چند ضلعی ها و مدل ها را به نقشه شما اضافه می کند.
  • کد را برای کنترل دوربین برای پرواز در نقشه و اطراف مکان های خاص پیاده سازی می کند.

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

  • چگونه بسته GoogleMaps3D را به برنامه Xcode SwiftUI اضافه کنیم.
  • نحوه مقداردهی اولیه نقشه سه بعدی با کلید API و نمای پیش فرض.
  • چگونه نشانگرها، مدل های سه بعدی، خطوط و چند ضلعی را به نقشه خود اضافه کنید.
  • چگونه دوربین را کنترل کنیم تا حرکت را به مکان دیگری متحرک کنیم.
  • نحوه مدیریت رویدادهای کلیک روی نشانگرهای مکان.

بعدش چی؟

  • برای جزئیات بیشتر درباره کارهایی که می‌توانید با Maps 3D SDK برای iOS انجام دهید، راهنمای توسعه‌دهنده را بررسی کنید.
  • با پاسخ دادن به نظرسنجی زیر به ما در ایجاد محتوایی که برای شما مفیدتر است کمک کنید:

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

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