使用 Google Maps Platform Navigation SDK 在 Swift 中构建简单的 iOS 导航应用

1. 准备工作

在此 Codelab 中,您将学习如何创建一个简单的 iOS 应用,该应用使用 Google Maps Platform Navigation SDK 导航到预配置的目的地。

完成后,您的应用将如下所示。

7e7c194a98d6dfa4.png

前提条件

学习内容

  • 如何创建一个简单的 iOS Swift 应用,该应用使用 Navigation SDK 导航到目的地。
  • 如何从远程 Cocoapods 代码库集成 Navigation SDK。
  • 如何管理位置信息权限以及 Navigation SDK 最终用户条款的用户同意书。
  • 如何初始化 SDK。
  • 如何设置目的地并启动导航指南。

所需条件

  • 最新的稳定版 XCode。
  • 启用了结算功能的 Google 账号和项目。
  • 一台在 XCode 模拟器中运行的 iOS 设备或模拟设备。无论您选择哪种方法,都必须满足 Navigation SDK 的最低要求

2. 进行设置

如果您还没有 Google Cloud Platform 账号和已启用结算功能的项目,请按照 Google Maps Platform 使用入门中的说明设置您的 Google Cloud 项目。

在控制台中选择一个 Google Cloud 项目

Cloud 控制台中,点击项目下拉菜单,然后选择要用于此 Codelab 的项目。

Google Cloud 控制台中的项目选择器下拉菜单。

在项目中启用 Navigation SDK

Google Cloud Marketplace 中启用此 Codelab 所需的 Google Maps Platform API 和 SDK。

前往 API 和服务 >库,并搜索“Navigation SDK”。

您应该会看到一个搜索结果。

Google Cloud 控制台中的“API 库”界面,其中显示了 Navigation SDK 页面。

点击 Navigation SDK 以打开“商品详情”页面。点击启用,为您的项目启用该 SDK。

针对 Google Maps SDK for iOS 重复此过程。

创建 API 密钥

在 Cloud Console 的凭据页面中,生成 API 密钥。向 Google Maps Platform 发出的所有请求都需要 API 密钥。控制台中的“凭据”页面上。点击页面顶部的“+创建凭据”,然后从选项中选择“API 密钥”。

对于生产环境使用,最佳实践是为 API 密钥设置应用限制,但对于本 Codelab,这并非必需操作。

3. 获取示例项目文件

本部分介绍如何通过从此 Codelab 的 GitHub 代码库中克隆文件来设置基本的空 XCode 应用项目。GitHub 代码库包含此 Codelab 代码的前后版本。本 Codelab 将从空白项目模板开始,逐步构建到完成状态。如果您遇到问题,可以使用代码库中已完成的项目作为参考。

克隆代码库或下载代码

导航到您要存储此 Codelab 的目录。

然后克隆代码库或下载代码:

git clone https://github.com/googlemaps-samples/codelab-navigation-101-ios-swift

如果您未安装 git,请点击此按钮获取代码:

为帮助您尽快入门,代码库的 Starter 文件夹中包含一些起始代码,可帮助您顺利完成此 Codelab。此外,还有已完成的 Solution 项目,便于您随时继续或查看您的进度。要使用解决方案项目,您需要按照“使用 Cocoapods 安装”然后运行“Pod 更新”solution/Navigation SDK Codelab 文件夹中的命令。

在本地克隆代码库后,使用 XCode 将 Starter 文件夹作为现有项目打开。检查项目是否能构建和运行。

连接设备或设置 XCode 模拟器

4. 将 Navigation SDK 添加到您的应用

您可以通过三种方法将 Navigation SDK 集成到 XCode 项目中:此 Codelab 使用 CocoaPods。如需详细了解如何使用 Swift Package Manager 进行集成或通过下载 SDK 进行手动安装,请参阅 Navigation SDK 文档中的创建 Xcode 项目并安装 Navigation SDK

使用 CocoaPods 安装

如果您还没有 CocoaPods 工具,请在 macOS 上从终端运行以下命令进行安装。如需了解详情,请参阅 CocoaPods 入门指南

sudo gem install cocoapods

在 starter/Navigation SDK Codelab 文件夹内的项目文件夹中创建一个名为 Podfile 的新文件(在 XCode 中,依次点击 File > New > File > Other > Empty,另存为“Podfile”)

将以下内容添加到您的 Podfile 中:

source 'https://github.com/CocoaPods/Specs.git'

platform :ios, '15.0'

target 'Navigation SDK Codelab' do
  pod 'GoogleNavigation', '9.1.1'
end

可享受 Podfile 的优惠。

打开一个终端,并将目录切换到您保存 Podfile 的位置(这应该是 Codelab 代码库中的“starter/Navigation SDK Codelab”文件夹)

cd "<path-to-starter-project-folder>/Navigation SDK Codelab"

运行 pod install 命令。这会安装 Podfile 中指定的 API 以及所有依赖项

pod install

关闭 Xcode,然后打开项目的 .xcworkspace 文件以启动 Xcode。从此刻开始,您必须使用 .xcworkspace 文件打开项目。

检查是否已将 Pods 目录添加到项目结构中,以及该目录是否包含“GoogleMaps”和“GoogleNavigation”Pod。

6e81772ee067d452.png

添加您的 API 密钥

按照以下方法向 AppDelegate.swift 添加 API 密钥:

  1. 添加以下 import 语句:
import GoogleMaps
import GoogleNavigation
  1. 将以下代码添加到 application(_:didFinishLaunchingWithOptions:) 方法中:
GMSServices.provideAPIKey("YOUR_API_KEY")

将“YOUR_API_KEY”替换为您在上一步中创建的 API 密钥。

构建项目并修正所有错误。

5. 配置应用权限

Navigation SDK 依赖 GPS 信号来提供道路贴靠位置和精细导航,因此您的应用需要请求用户授予访问精确位置数据的权限。

为此,您需要在 Xcode 中向应用的 Info.plist 添加一些属性,向应用添加一些代码以在运行时向用户请求权限,并处理任何错误(例如未授予权限或位置信息不可用)。

在 Xcode 中打开 Info.plist。看上去应该类似这样

6532a85bd9ac8fb4

请求确切位置信息权限

您可以通过将鼠标指针悬停在“信息属性列表”上来添加新值。行,直到您看到“+”号图标。点击“+”即可看到一个包含建议的属性名称的对话框,但请注意,您也可以手动添加属性。

将以下属性和值添加到 Info.plist:

属性

“隐私 - 位置信息始终和使用时”使用情况说明

“此应用需要获取您的设备位置信息,才能提供精细导航路线”

隐私权 - 使用位置信息时的使用情况说明

“此应用需要获取您的设备位置信息,才能提供精细导航路线”

allowsBackgroundLocationUpdates

请求后台位置信息权限

将以下属性和值添加到 Info.plist:

UIBackgroundModes > 添加行 > Item 0: App registers for location updates(从建议的下拉列表中选择此值)

完成后,Info.plist 应如下所示。

3b0c49018451d0ff.png

在运行时请求位置信息访问权限

将以下 import 语句添加到 ViewController.swift

import GoogleNavigation

将以下声明添加到 ViewController 类中:

var locationManager: CLLocationManager!

loadView() 添加方法替换项并调用 locationManager.requestAlwaysAuthorization()

override func loadView() {
        locationManager = CLLocationManager()
        locationManager.requestAlwaysAuthorization()

现在,您的应用程序将向用户请求位置信息,并在用户授予 权限后允许应用程序使用位置信息 。

请求显示通知的权限

将以下代码添加到 loadView() 中,以向用户请求显示通知的权限,这是显示导航操作说明所必需的。

UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) {
        granted, error in
        // Handle denied authorization to display notifications.
          if !granted || error != nil {
              print("User rejected request to display notifications.")
          }
        }

构建并运行应用,检查系统是否提示您分享位置信息并启用通知。

ad5f665a21170c49.png

6. 添加导航界面

在此步骤中,您将添加一张地图并将其配置为显示位置。然后,您将向用户显示一个包含 Navigation SDK 使用条款的对话框。

向应用添加地图视图

添加以下代码行,以在您的 ViewController 中声明 GMSMapView 变量。

var mapView: GMSMapView!

将以下代码添加到 Viewcontroller.swift 中的 loadView() 以初始化地图。

let camera = GMSCameraPosition.camera(withLatitude: 51.483174, longitude: -0.177369, zoom: 14)
let options = GMSMapViewOptions()
options.camera = camera
options.frame = .zero
        
mapView = GMSMapView(options: options)
view = mapView

构建并运行应用,您应该会看到一个以伦敦西南为中心的地图。

1d46ce5c0851cae3.png

显示 Navigation SDK 产品使用条款对话框

将以下代码添加到与上一个代码相同的 loadView() 方法的 ViewController.swift 中。系统会显示 Navigation SDK 最终用户使用条款。如果未被接受,系统将不会启用导航。

// Show the terms and conditions.
let companyName = "Navigation SDK Codelab"
GMSNavigationServices.showTermsAndConditionsDialogIfNeeded(withCompanyName: companyName) { termsAccepted in
  if termsAccepted {
    // Enable navigation if the user accepts the terms.
    self.mapView.isNavigationEnabled = true
    // Request authorization for alert notifications which deliver guidance instructions
    // in the background.
  } else {
    // Handle the case when the user rejects the terms and conditions.
  }
}

构建并运行应用,即可看到对话框。

29f17ae5b4c07c9f

7. 为按键导航事件添加监听器

此步骤将向您介绍如何为关键事件(例如到达目的地或司机重选路线)设置监听器。

如需监听这些事件,您的视图控制器必须采用 GMSNavigatorListener 协议。

将此协议添加到 ViewController.swift 中的类定义。

class ViewController: UIViewController,
                      GMSNavigatorListener {

现在,添加一行代码以在 loadView(): 中设置监听器

// Add a listener for GMSNavigator.
mapView.navigator?.add(self)

最后,向您的类添加两个方法来处理所引发的事件。

// Listener to handle arrival events.
func navigator(_ navigator: GMSNavigator, didArriveAt waypoint: GMSNavigationWaypoint) {
  print("You have arrived at: \(waypoint.title)")
}

// Listener for route change events.
func navigatorDidChangeRoute(_ navigator: GMSNavigator) {
  print("The route has changed.")
}

8. 设置目的地并开始导航

本部分将介绍如何设置目的地并启动导航指引。

为导航逻辑创建一个新函数。

首先,向 ViewController 添加一个名为 startNav() 的新函数。该文件将包含用于设置目的地并开始导航的逻辑。

// Create a route and start guidance.
@objc func startNav() {
}

为目标位置创建一个 Waypoint

接下来,创建包含单个航点的目的地数组。

// Create a route and start guidance.
@objc func startNav() {
  var destinations = [GMSNavigationWaypoint]()
  destinations.append(
    GMSNavigationWaypoint.init(
      placeID: "ChIJH-tBOc4EdkgRJ8aJ8P1CUxo",
      title: "Trafalgar Square")!)
}

调用 setDestinations()并处理响应。

接下来,调用 setDestinations 并处理返回的 GMSRouteStatus

如果 GMSRouteStatus 为“OK”,则通过在 mapViewnavigator 对象上设置 isGuidanceActive=true 来启动导航。否则,输出一条语句以表明存在错误。

如果返回的 GMSRouteStatus 值为“OK”,则通过调用 mapView.locationSimulator.simulateLocationsAlongExistingRoute() 开始模拟沿路线驾驶。

// Create a route and start guidance.
@objc func startNav() {
  var destinations = [GMSNavigationWaypoint]()
    destinations.append(
      GMSNavigationWaypoint.init(
        placeID: "ChIJH-tBOc4EdkgRJ8aJ8P1CUxo",
          title: "Trafalgar Square")!)
      
  mapView.navigator?.setDestinations(
    destinations
  ) { routeStatus in
    guard routeStatus == .OK else {
      print("Handle route statuses that are not OK.")
      return
    }
    //If routeStatus is OK, start guidance.
    self.mapView.navigator?.isGuidanceActive = true
    //start simulating driving along the route. self.mapView.locationSimulator?.simulateLocationsAlongExistingRoute()
    self.mapView.cameraMode = .following
  }
}

处理常见的错误状态

更明确地处理 GMSRouteStatus 错误很有用,尤其是在调试新应用的初始问题时。例如,您可能会发现,由于调试设置,您最初会更频繁地收到位置信息权限、API 密钥或“未找到路线”错误,因此处理这些错误状态会很有用。

添加用于处理这些特定情况的代码,并向控制台输出一个语句。

mapView.navigator?.setDestinations(
  destinations
) { routeStatus in
    guard routeStatus == .OK else {
      print("Handle route statuses that are not OK.")
      switch routeStatus {
       case .locationUnavailable:
        print("Location unavailable.") //check permissions
      case .noRouteFound:
        print("No route found.") //check start location and destination
      case .waypointError:
        print("Waypoint error") //check Place ID
      default:
        print("Not sure what happened")
      }
    return
  }

添加一个按钮以开始导航指南

最后,向界面添加一个按钮,并将其连接到 startNav 方法。使用以下代码创建一个名为 makeButton() 的方法。从 loadView() 调用 makeButton() 函数。

// Add a button to the view.
func makeButton() {
  // A button to start navigation.
  let navButton = UIButton(frame: CGRect(x: 5, y: 150, width: 200, height: 35))
  navButton.backgroundColor = .blue
  navButton.alpha = 0.5
  navButton.setTitle("Start navigation", for: .normal)
  navButton.addTarget(self, action: #selector(startNav), for: .touchUpInside)
  self.mapView.addSubview(navButton)
}

构建并运行您的应用。

注意:在以下位置运行代码:

startNav()

将调用

setDestinations()

方法,这会在使用的 1000 个目的地之后产生费用。如需了解详情,请参阅用量和结算

9. 恭喜!

太棒了,您已到达目的地!

7a69dcb75c904d7.png

您已使用 Google Maps Platform Navigation SDK 创建了一个简单的应用,该应用可为目的地提供精细导航指导。

您已配置应用权限和 Navigation SDK 最终用户条款对话框,并使用地点 ID 指定了目的地。您已在应用中处理各种成功和错误状态。

10. 更进一步

如果您想进一步开发应用,请参阅以下主题以获取灵感。

  • 监听更多导航事件。添加代码,以在剩余时间或距离超过阈值时显示消息。
  • 自定义导航界面
  • 如果您想挑战更大的挑战,不妨看看是否可以添加 Places API 地点选取器,让用户设置目的地。提示:Navigation SDK 演示版应用提供了示例实现。在项目文件夹中运行 pod try GoogleNavigation 以查看代码。