12. CarPlay导航应用开发:CPMapTemplate、路线规划、导航指令显示

说实话,CarPlay导航开发这块,我当年刚接触的时候也踩过不少坑。特别是 CPMapTemplate 这个核心类,看着文档挺简单,真上手才发现细节特别多。今天咱们就把它彻底讲透。

12.1 CPMapTemplate 是什么?

CPMapTemplate 是 CarPlay 导航应用的画布。你想想看,用户开车时看到的那个地图界面,本质上就是这个模板在管理。它负责显示地图、路线、导航指令,还有底部的操作栏。

我个人习惯把 CPMapTemplate 理解成「导航的舞台」。所有跟导航相关的 UI 元素,都得在这个舞台上布置。它不像 CPListTemplate 那样简单,因为导航场景下,用户的操作是动态的、实时的。

核心要点: CPMapTemplate 是 CarPlay 导航应用的根视图,所有导航相关的 UI 组件都挂载在它上面。

12.2 创建 CPMapTemplate 实例

创建其实不难,但有几个参数你得注意。看代码:

// 创建地图模板
let mapTemplate = CPMapTemplate()

// 设置导航会话的代理
mapTemplate.mapDelegate = self

// 配置底栏按钮
let startButton = CPBarButton(type: .text) { button in
    // 开始导航的逻辑
}
startButton.title = "开始导航"
mapTemplate.leadingNavigationBarButtons = [startButton]

// 设置地图的可见区域
let center = CLLocationCoordinate2D(latitude: 39.9042, longitude: 116.4074)
let span = MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
let region = MKCoordinateRegion(center: center, span: span)
mapTemplate.setVisibleRegion(region, animated: false)

嗯,这里要注意:mapDelegate 必须实现 CPMapTemplateDelegate 协议。我在项目中遇到过,有人忘了设置代理,结果导航指令死活不显示。排查了半天才发现是代理没连上。

12.3 路线规划:从 A 到 B 的核心逻辑

路线规划说白了就是算路。CarPlay 本身不负责算路,它只负责展示。真正的路线计算,你得用 MKDirections 或者自己的后端服务。

我建议的做法是:

  1. 用户输入目的地后,先调自己的路线引擎算路
  2. 把算好的路线封装成 CPRoute 对象
  3. 通过 startNavigationSession 启动导航会话

看一个完整的例子:

// 1. 计算路线
let request = MKDirections.Request()
request.source = MKMapItem(placemark: MKPlacemark(coordinate: startCoord))
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: endCoord))
request.transportType = .automobile

let directions = MKDirections(request: request)
directions.calculate { response, error in
    guard let route = response?.routes.first else { return }
    
    // 2. 创建 CPRoute
    let cpRoute = CPRoute(
        route: route,
        routeChoice: CPRouteChoice(
            summaryVariants: ["推荐路线", "约30分钟"],
            additionalInformationVariants: nil,
            selectionSummaryVariants: nil
        )
    )
    
    // 3. 启动导航会话
    let session = mapTemplate.startNavigationSession(for: cpRoute)
    session.upcomingManeuvers = self.generateManeuvers(from: route)
}
我的经验: 路线规划时,最好给用户提供 2-3 条备选路线。CarPlay 的 CPRouteChoice 支持多条路线选择,用户可以通过旋钮或触摸切换。我做过一个项目,只给了一条路线,结果用户反馈说「我想走高速,你偏给我走小路」。

12.4 导航指令显示:让用户知道怎么走

导航指令是 CarPlay 导航体验的关键。你想想看,用户开车时不可能盯着地图看,他们需要的是清晰的语音和文字提示。

CPManeuver 就是用来描述每个导航动作的。它包含:

  • maneuverType:动作类型(左转、右转、环岛等)
  • junctionType:路口类型
  • instructionVariants:指令文本(支持多语言)
  • initialHeading:初始方向
  • finalHeading:最终方向

我曾经犯过一个错误:只设置了 instructionVariants,没设置 maneuverType。结果 CarPlay 显示了一个空箭头,用户完全看不懂。后来我养成了习惯,每个 CPManeuver 都完整配置。

// 创建导航指令
let maneuver = CPManeuver()
maneuver.maneuverType = .turnLeft
maneuver.instructionVariants = ["前方500米左转", "500米后左转"]
maneuver.initialHeading = 90
maneuver.finalHeading = 180

// 设置到导航会话
session.upcomingManeuvers = [maneuver]

// 更新当前指令
session.currentManeuver = maneuver
注意: instructionVariants 数组的顺序很重要。CarPlay 会根据当前语言环境自动选择最合适的指令。如果你只提供一个中文指令,那英文用户就看不到任何文字。我建议至少提供中英文两种。

12.5 导航会话的生命周期管理

导航会话不是永久的。用户可能中途取消导航,或者到达目的地后自动结束。你需要监听这些事件:

事件 回调方法 说明
导航开始 mapTemplateDidBeginNavigation 会话已启动,可以开始更新指令
导航结束 mapTemplateDidEndNavigation 用户主动取消或到达目的地
路线更新 mapTemplate:didUpdateNavigationSession: 路线重新规划时触发
指令变更 mapTemplate:didUpdateManeuver: 当前导航指令发生变化

我个人习惯在 mapTemplateDidEndNavigation 里做一些清理工作,比如停止位置更新、释放资源。不然的话,导航结束了还在后台跑定位,用户电量会掉得很快。

12.6 知识体系总览

下面这张图是我自己整理的,把 CarPlay 导航开发的核心脉络画出来了。你照着这个思路走,基本不会迷路。

CarPlay 导航应用开发核心流程 CPMapTemplate 路线规划 (MKDirections / 自研引擎) 导航会话 (CPNavigationSession) 导航指令显示 (CPManeuver + 语音提示) 生命周期管理 • 开始导航 • 更新路线 • 指令变更 • 结束导航 • 清理资源 核心组件:CPMapTemplate → CPRoute → CPNavigationSession → CPManeuver

12.7 避坑指南

最后,分享几个我实际项目中踩过的坑:

  • 地图模板只能有一个:CarPlay 不允许同时存在多个 CPMapTemplate。如果你 push 了新的地图模板,旧的会被自动销毁。我之前想搞个「分屏导航」,结果发现根本行不通。
  • 导航指令要提前生成:别等到用户快到路口了才生成指令。CarPlay 需要提前缓存,否则会显示空白。我建议在导航开始时,就把整条路线的所有指令都生成好。
  • 语音提示要配合CPManeuver 只负责显示,语音提示你得自己用 AVSpeechSynthesizer 播报。CarPlay 不会自动帮你读指令。
  • 测试时用模拟器:真机调试 CarPlay 导航很麻烦。我一般先用模拟器跑通逻辑,再上真机验证。模拟器里可以用 CLLocation 模拟位置变化。
小技巧: 如果你想让导航指令更清晰,可以在 CPManeuveruserInfo 里塞一些自定义数据,比如「这个路口有摄像头」之类的。虽然 CarPlay 不会显示,但你可以自己在语音提示里用。

好了,这一章的内容就到这。CarPlay 导航开发的核心就是 CPMapTemplate + 路线规划 + 指令显示,三者缺一不可。你只要把这三个环节打通,剩下的就是细节打磨了。