使用 SwiftUI (Swift) 在 iOS 應用程式中加入地圖

1. 事前準備

本程式碼研究室將教導您如何搭配 SwiftUI 使用 Maps SDK for iOS。

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

必要條件

  • 具備 Swift 基本知識
  • 熟悉 SwiftUI 基本概念

學習內容

  • 啟用並使用 Maps SDK for iOS,透過 SwiftUI 將 Google 地圖新增至 iOS 應用程式。
  • 在地圖中加入標記。
  • 在 SwiftUI 和 GMSMapView 物件之間傳遞狀態。

軟硬體需求

2. 做好準備

在下一個啟用步驟中,請啟用 Maps SDK for iOS

設定 Google 地圖平台

如果您尚未建立 Google Cloud Platform 帳戶,以及啟用計費功能的專案,請參閱「開始使用 Google 地圖平台」指南,建立帳單帳戶和專案。

  1. Cloud 控制台中,按一下專案下拉式選單,然後選取要用於本程式碼研究室的專案。

  1. Google Cloud Marketplace 中,啟用本程式碼研究室所需的 Google 地圖平台 API 和 SDK。如要瞭解如何操作,請觀看這部影片或參閱這份說明文件
  2. 在 Cloud Console 的「憑證」頁面中產生 API 金鑰。你可以按照這部影片這份文件中的步驟操作。所有 Google 地圖平台要求都需要 API 金鑰。

3. 下載範例程式碼

為協助您盡快上手,我們提供一些範例程式碼,方便您跟著本程式碼研究室的說明操作。歡迎直接前往解決方案,但如果您想按照所有步驟自行建構,請繼續閱讀。

  1. 如果已安裝 git,請複製存放區。
git clone https://github.com/googlecodelabs/maps-ios-swiftui.git

或者,您也可以點選下列按鈕下載原始碼。

  1. 取得驗證碼後,在終端機中 cdstarter/GoogleMapsSwiftUI 目錄。
  2. 執行 carthage update --platform iOS,下載 Maps SDK for iOS
  3. 最後,在 Xcode 中開啟 GoogleMapsSwiftUI.xcodeproj 檔案

4. 程式碼總覽

在您下載的初始專案中,系統已為您提供並實作下列類別:

  • AppDelegate - 應用程式的 UIApplicationDelegate。您將在此處初始化 Maps SDK for iOS。
  • City - 代表城市的結構體 (包含城市名稱和座標)。
  • MapViewController - 範圍縮小的 UIKit UIViewController,內含 Google 地圖 (GMSMapView)
    • SceneDelegate - 應用程式的 UIWindowSceneDelegateContentView 是從中例項化。

此外,下列類別已部分實作,您將在本程式碼研究室結束前完成這些類別:

  • ContentView - 包含應用程式的頂層 SwiftUI 檢視區塊。
  • MapViewControllerBridge - 將 UIKit 檢視區塊橋接至 SwiftUI 檢視區塊的類別。具體來說,這個類別可讓 SwiftUI 存取 MapViewController

5. SwiftUI 與 UIKit

iOS 13 推出了 SwiftUI,做為開發 iOS 應用程式時,取代 UIKit 的 UI 架構。與前身 UIKit 相比,SwiftUI 有許多優點。例如:

  • 狀態變更時,檢視畫面會自動更新。使用名為「狀態」的物件,系統會在物件所含基礎值有任何變更時,自動更新 UI。
  • 即時預覽功能可加快開發速度。即時預覽功能可減少將程式碼建構及部署至模擬器的需求,因為您可以在 Xcode 上輕鬆預覽 SwiftUI 檢視畫面,查看視覺變化。
  • 可靠資料來源位於 Swift 中。SwiftUI 中的所有檢視區塊都是以 Swift 宣告,因此不再需要使用介面建構器。
  • 可與 UIKit 互通。與 UIKit 的互通性可確保現有應用程式能逐步使用 SwiftUI 和現有檢視區塊。此外,您仍可在 SwiftUI 中使用尚不支援 SwiftUI 的程式庫,例如 Maps SDK for iOS

但也有一些缺點:

  • SwiftUI 僅適用於 iOS 13 以上版本。
  • 無法在 Xcode 預覽畫面中檢查檢視區塊階層。

SwiftUI 狀態和資料流程

SwiftUI 提供全新的 UI 建立方式,採用宣告式方法,您只要告訴 SwiftUI 檢視區塊的外觀和所有不同狀態,系統就會完成其餘工作。每當基礎狀態因事件或使用者動作而變更時,SwiftUI 就會更新檢視區塊。這種設計通常稱為「單向資料流」。本程式碼研究室不會深入探討這項設計的具體細節,但建議您閱讀 Apple 的狀態和資料流說明文件,瞭解這項設計的運作方式。

使用 UIViewRepresentable 或 UIViewControllerRepresentable 橋接 UIKit 和 SwiftUI

由於 Maps SDK for iOS 是以 UIKit 為基礎建構,且未提供與 SwiftUI 相容的檢視區塊,因此在 SwiftUI 中使用時,必須符合 UIViewRepresentableUIViewControllerRepresentable。這些通訊協定可讓 SwiftUI 分別納入以 UIKit 建構的 UIViewUIViewController。雖然您可以使用任一通訊協定,在 SwiftUI 檢視區塊中新增 Google 地圖,但我們會在下一個步驟中,瞭解如何使用 UIViewControllerRepresentable 納入含有地圖的 UIViewController

6. 新增地圖

在本節中,您將在 SwiftUI 檢視區塊中加入 Google 地圖。

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

新增 API 金鑰

您必須將先前步驟中建立的 API 金鑰提供給 Maps SDK for iOS,才能將帳戶與應用程式中顯示的地圖建立關聯。

如要提供 API 金鑰,請開啟 AppDelegate.swift 檔案,然後前往 application(_, didFinishLaunchingWithOptions) 方法。SDK 是使用 GMSServices.provideAPIKey() 和「YOUR_API_KEY」字串初始化。然後將該字串替換成您的 API 金鑰。完成這個步驟後,應用程式啟動時就會初始化 Maps SDK for iOS。

使用 MapViewControllerBridge 新增 Google 地圖

現在 API 金鑰已提供給 SDK,下一步是在應用程式中顯示地圖。

範例程式碼中提供的檢視區塊控制器 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 屬性中已提供並實作 ZStackZStack 包含可互動及可拖曳的城市清單,您會在後續步驟中使用。目前,請在 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

標記做為狀態

宣告名為 markers 的屬性,這個屬性是 GMSMarker 清單,代表 cities 靜態屬性中宣告的每個城市。ContentView請注意,這個屬性會使用 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 將 State 傳遞至 MapViewControllerBridge

除了顯示 markers 屬性資料的城市清單外,請將這個屬性傳遞至 MapViewControllerBridge 結構體,以便在 Google 地圖上顯示這些標記。請按照下列步驟操作:

  1. MapViewControllerBridge 中宣告以 @Binding 註解的新 markers 屬性

MapViewControllerBridge

struct MapViewControllerBridge: : UIViewControllerRepresentable {
  @Binding var markers: [GMSMarker]
  // ...
}
  1. MapViewControllerBridge 中,更新 updateUIViewController(_, context) 方法以使用 markers 屬性

如上一步所述,每當狀態變更時,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. ContentViewmarkers 屬性傳遞至 MapViewControllerBridge

由於您在 MapViewControllerBridge 中新增了屬性,因此現在必須在 MapViewControllerBridge 的初始值中傳遞這個屬性的值。因此,如果您嘗試建構應用程式,應該會發現應用程式無法編譯。如要修正這個問題,請更新 ContentView,建立 MapViewControllerBridge 並傳入 markers 屬性,如下所示:

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

請注意,由於 MapViewControllerBridge 預期會收到繫結屬性,因此使用前置字串 $markers 傳遞至 MapViewControllerBridge$ 是預留的前置字元,用於 Swift 屬性包裝函式。如果套用至 State,則會傳回 Binding

  1. 請執行應用程式,查看地圖上顯示的標記。

8. 為所選城市製作動畫

在上一個步驟中,您已將「狀態」從一個 SwiftUI 檢視區塊傳遞至另一個檢視區塊,在地圖上新增標記。在這個步驟中,您會在互動式清單中輕觸城市或標記後,將動畫效果套用至該城市或標記。如要執行動畫,您必須在發生變更時修改地圖的攝影機位置,藉此對 State 的變更做出反應。如要進一步瞭解地圖攝影機的概念,請參閱「攝影機和檢視畫面」。

animate-city@2x.png

將地圖動畫效果套用至所選城市

如要將地圖動畫效果套用至所選城市,請按照下列步驟操作:

  1. MapViewControllerBridge 中定義新的繫結

ContentView 具有名為 selectedMarker 的 State 屬性,該屬性會初始化為 nil,並在清單中選取城市時更新。這項作業是由 ContentView 內的 CitiesList 檢視區塊 buttonAction 處理。

ContentView

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

每當 selectedMarker 變更時,MapViewControllerBridge 應留意這項狀態變更,以便將地圖動畫設為所選標記。因此,請在 MapViewControllerBridge 中定義 GMSMarker 類型的全新繫結,並將屬性命名為 selectedMarker

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  @Binding var selectedMarker: GMSMarker?
}
  1. 更新 MapViewControllerBridge,在 selectedMarker 變更時為地圖加上動畫效果

宣告新的繫結後,您需要更新 MapViewControllerBridgeupdateUIViewController_, 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) 函式會使用 GMSMapViewanimate(with) 函式執行一連串的地圖動畫。

  1. ContentViewselectedMarker 傳遞至 MapViewControllerBridge

MapViewControllerBridge 宣告新的繫結後,請繼續更新 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. 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 閉包,在 ContentView 中執行個體化 MapViewControllerBridge。複製並貼上下列程式碼,新增名為 zoomInCenter 的 State,並使用 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 導入了「協調器」的概念,專門用來做為 UIKit 檢視區塊控制器的委派。因此在 SwiftUI 世界中,協調器應負責遵守 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) 中的檢視區塊控制器初始化作業。您可透過 Context 物件存取上一個步驟中建立的協調器。

MapViewControllerBridge

struct MapViewControllerBridge: UIViewControllerRepresentable {
  // ...
  func makeUIViewController(context: Context) -> MapViewController {
    let uiViewController = MapViewController()
    uiViewController.map.delegate = context.coordinator
    return uiViewController
  }
}
  1. MapViewControllerBridge 中新增閉包,攝影機移動事件即可向上傳播

由於目標是使用攝影機移動更新檢視區塊,因此請在 MapViewControllerBridge 中宣告新的閉包屬性,接受名為 mapViewWillMove 的布林值,並在 MapViewCoordinator 內的委派方法 mapView(_, willMove) 中叫用這個閉包。將 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,傳遞這個新結尾的值。在該閉包中,如果移動事件與手勢相關,請將「State」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. 恭喜

恭喜你完成目前為止的工作!您已學到許多內容,希望這些課程能幫助您使用 Maps SDK for iOS 建構自己的 SwiftUI 應用程式。

您學到的內容

後續步驟

  • Maps SDK for iOS
    • Maps SDK for iOS 的官方說明文件
  • Places SDK for iOS:尋找您附近的當地商家和搜尋點
  • maps-sdk-for-ios-samples
    • GitHub 上的程式碼範例,示範 Maps SDK for iOS 的所有功能。
  • SwiftUI - Apple 官方的 SwiftUI 說明文件
  • 請填寫下列問卷調查,協助我們製作最實用的內容:

你還想看到哪些程式碼研究室?

在地圖上以視覺化方式呈現資料 進一步瞭解如何自訂地圖樣式 在地圖中建構 3D 互動

找不到最感興趣的程式碼研究室嗎?請在這裡提出新的問題