carplay

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

CarPlay

CarPlay

Build apps that display on the vehicle's CarPlay screen using the CarPlay framework's template-based UI system. Covers scene lifecycle, template types, navigation guidance, audio playback, communication, point-of-interest categories, entitlement setup, and simulator testing. Targets Swift 6.3 / iOS 26+.
See references/carplay-patterns.md for extended patterns including full navigation sessions, dashboard scenes, and advanced template composition.
使用CarPlay框架的基于模板的UI系统构建可在车载CarPlay屏幕上显示的应用。内容涵盖场景生命周期、模板类型、导航指引、音频播放、通讯、兴趣点分类、权限设置以及模拟器测试。目标环境为Swift 6.3 / iOS 26+。
查看 references/carplay-patterns.md 获取扩展模式,包括完整导航会话、仪表盘场景和高级模板组合。

Contents

目录

Entitlements and Setup

权限与设置

CarPlay requires a category-specific entitlement granted by Apple. Request it at developer.apple.com/contact/carplay and agree to the CarPlay Entitlement Addendum.
CarPlay需要苹果授予的特定分类权限。请在 developer.apple.com/contact/carplay 申请,并同意《CarPlay权限附加条款》。

Entitlement Keys by Category

按分类划分的权限密钥

EntitlementCategory
com.apple.developer.carplay-audio
Audio
com.apple.developer.carplay-communication
Communication
com.apple.developer.carplay-maps
Navigation
com.apple.developer.carplay-charging
EV Charging
com.apple.developer.carplay-parking
Parking
com.apple.developer.carplay-quick-ordering
Quick Food Ordering
权限分类
com.apple.developer.carplay-audio
音频
com.apple.developer.carplay-communication
通讯
com.apple.developer.carplay-maps
导航
com.apple.developer.carplay-charging
电动车充电
com.apple.developer.carplay-parking
停车
com.apple.developer.carplay-quick-ordering
快速点餐

Project Configuration

项目配置

  1. Update the App ID in the developer portal under Additional Capabilities.
  2. Generate a new provisioning profile for the updated App ID.
  3. In Xcode, disable automatic signing and import the CarPlay provisioning profile.
  4. Add an
    Entitlements.plist
    with the entitlement key set to
    true
    .
  5. Set Code Signing Entitlements build setting to the
    Entitlements.plist
    path.
  1. 在开发者门户的“额外功能”下更新App ID。
  2. 为更新后的App ID生成新的配置文件。
  3. 在Xcode中关闭自动签名,导入CarPlay配置文件。
  4. 添加
    Entitlements.plist
    文件,并将对应权限密钥设为
    true
  5. 将“代码签名权限”构建设置设置为
    Entitlements.plist
    的路径。

Key Types

核心类型

TypeRole
CPTemplateApplicationScene
UIScene subclass for the CarPlay display
CPTemplateApplicationSceneDelegate
Scene connect/disconnect lifecycle
CPInterfaceController
Manages the template navigation hierarchy
CPTemplate
Abstract base for all CarPlay templates
CPSessionConfiguration
Vehicle display limits and content style
类型作用
CPTemplateApplicationScene
用于CarPlay显示的UIScene子类
CPTemplateApplicationSceneDelegate
场景连接/断开的生命周期管理
CPInterfaceController
管理模板导航层级
CPTemplate
所有CarPlay模板的抽象基类
CPSessionConfiguration
车载显示屏限制与内容样式

Scene Configuration

场景配置

Declare the CarPlay scene in
Info.plist
and implement
CPTemplateApplicationSceneDelegate
to respond when CarPlay connects.
Info.plist
中声明CarPlay场景,并实现
CPTemplateApplicationSceneDelegate
以响应CarPlay连接事件。

Info.plist Scene Manifest

Info.plist场景清单

plist
<key>UIApplicationSceneManifest</key>
<dict>
    <key>UIApplicationSupportsMultipleScenes</key>
    <true/>
    <key>UISceneConfigurations</key>
    <dict>
        <key>CPTemplateApplicationSceneSessionRoleApplication</key>
        <array>
            <dict>
                <key>UISceneClassName</key>
                <string>CPTemplateApplicationScene</string>
                <key>UISceneConfigurationName</key>
                <string>CarPlaySceneConfiguration</string>
                <key>UISceneDelegateClassName</key>
                <string>$(PRODUCT_MODULE_NAME).CarPlaySceneDelegate</string>
            </dict>
        </array>
    </dict>
</dict>
plist
<key>UIApplicationSceneManifest</key>
<dict>
    <key>UIApplicationSupportsMultipleScenes</key>
    <true/>
    <key>UISceneConfigurations</key>
    <dict>
        <key>CPTemplateApplicationSceneSessionRoleApplication</key>
        <array>
            <dict>
                <key>UISceneClassName</key>
                <string>CPTemplateApplicationScene</string>
                <key>UISceneConfigurationName</key>
                <string>CarPlaySceneConfiguration</string>
                <key>UISceneDelegateClassName</key>
                <string>$(PRODUCT_MODULE_NAME).CarPlaySceneDelegate</string>
            </dict>
        </array>
    </dict>
</dict>

Scene Delegate (Non-Navigation)

场景代理(非导航类)

Non-navigation apps receive an interface controller only. No window.
swift
import CarPlay

final class CarPlaySceneDelegate: UIResponder,
    CPTemplateApplicationSceneDelegate {

    var interfaceController: CPInterfaceController?

    func templateApplicationScene(
        _ templateApplicationScene: CPTemplateApplicationScene,
        didConnect interfaceController: CPInterfaceController
    ) {
        self.interfaceController = interfaceController
        interfaceController.setRootTemplate(buildRootTemplate(),
                                            animated: true, completion: nil)
    }

    func templateApplicationScene(
        _ templateApplicationScene: CPTemplateApplicationScene,
        didDisconnectInterfaceController interfaceController: CPInterfaceController
    ) {
        self.interfaceController = nil
    }
}
非导航类应用仅会收到界面控制器,没有窗口。
swift
import CarPlay

final class CarPlaySceneDelegate: UIResponder,
    CPTemplateApplicationSceneDelegate {

    var interfaceController: CPInterfaceController?

    func templateApplicationScene(
        _ templateApplicationScene: CPTemplateApplicationScene,
        didConnect interfaceController: CPInterfaceController
    ) {
        self.interfaceController = interfaceController
        interfaceController.setRootTemplate(buildRootTemplate(),
                                            animated: true, completion: nil)
    }

    func templateApplicationScene(
        _ templateApplicationScene: CPTemplateApplicationScene,
        didDisconnectInterfaceController interfaceController: CPInterfaceController
    ) {
        self.interfaceController = nil
    }
}

Scene Delegate (Navigation)

场景代理(导航类)

Navigation apps receive both an interface controller and a
CPWindow
. Set the window's root view controller to draw map content.
swift
func templateApplicationScene(
    _ templateApplicationScene: CPTemplateApplicationScene,
    didConnect interfaceController: CPInterfaceController,
    to window: CPWindow
) {
    self.interfaceController = interfaceController
    self.carWindow = window
    window.rootViewController = MapViewController()

    let mapTemplate = CPMapTemplate()
    mapTemplate.mapDelegate = self
    interfaceController.setRootTemplate(mapTemplate, animated: true,
                                        completion: nil)
}
导航类应用会同时收到界面控制器和
CPWindow
。设置窗口的根视图控制器以绘制地图内容。
swift
func templateApplicationScene(
    _ templateApplicationScene: CPTemplateApplicationScene,
    didConnect interfaceController: CPInterfaceController,
    to window: CPWindow
) {
    self.interfaceController = interfaceController
    self.carWindow = window
    window.rootViewController = MapViewController()

    let mapTemplate = CPMapTemplate()
    mapTemplate.mapDelegate = self
    interfaceController.setRootTemplate(mapTemplate, animated: true,
                                        completion: nil)
}

Templates Overview

模板概述

CarPlay provides a fixed set of template types. The app supplies content; the system renders it on the vehicle display.
CarPlay提供一组固定的模板类型。应用提供内容,系统负责在车载显示屏上渲染。

General Purpose Templates

通用模板

TemplatePurpose
CPTabBarTemplate
Container with tabbed child templates
CPListTemplate
Scrollable sectioned list
CPGridTemplate
Grid of tappable icon buttons (max 8)
CPInformationTemplate
Key-value info with up to 3 actions
CPAlertTemplate
Modal alert with up to 2 actions
CPActionSheetTemplate
Modal action sheet
模板用途
CPTabBarTemplate
包含标签页子模板的容器
CPListTemplate
可滚动的分段列表
CPGridTemplate
可点击图标按钮网格(最多8个)
CPInformationTemplate
带最多3个操作的键值信息展示
CPAlertTemplate
带最多2个操作的模态弹窗
CPActionSheetTemplate
模态操作表

Category-Specific Templates

分类专属模板

TemplateCategory
CPMapTemplate
Navigation -- map overlay with nav bar
CPSearchTemplate
Navigation -- destination search
CPNowPlayingTemplate
Audio -- shared Now Playing screen
CPPointOfInterestTemplate
EV Charging / Parking / Food -- POI map
CPContactTemplate
Communication -- contact card
模板分类
CPMapTemplate
导航类——带导航栏的地图覆盖层
CPSearchTemplate
导航类——目的地搜索
CPNowPlayingTemplate
音频类——共享的正在播放屏幕
CPPointOfInterestTemplate
电动车充电/停车/点餐类——兴趣点地图
CPContactTemplate
通讯类——联系人卡片

Navigation Hierarchy

导航层级

Use
pushTemplate(_:animated:completion:)
to add templates to the stack. Use
presentTemplate(_:animated:completion:)
for modal display. Use
popTemplate(animated:completion:)
to go back.
CPTabBarTemplate
must be set as root -- it cannot be pushed or presented.
使用
pushTemplate(_:animated:completion:)
将模板添加到栈中。 使用
presentTemplate(_:animated:completion:)
展示模态模板。 使用
popTemplate(animated:completion:)
返回上一级。
CPTabBarTemplate
必须设为根模板——不能被推入或模态展示。

CPTabBarTemplate

CPTabBarTemplate示例

swift
let browseTab = CPListTemplate(title: "Browse",
                               sections: [CPListSection(items: listItems)])
browseTab.tabImage = UIImage(systemName: "list.bullet")

let tabBar = CPTabBarTemplate(templates: [browseTab, settingsTab])
tabBar.delegate = self
interfaceController.setRootTemplate(tabBar, animated: true, completion: nil)
swift
let browseTab = CPListTemplate(title: "Browse",
                               sections: [CPListSection(items: listItems)])
browseTab.tabImage = UIImage(systemName: "list.bullet")

let tabBar = CPTabBarTemplate(templates: [browseTab, settingsTab])
tabBar.delegate = self
interfaceController.setRootTemplate(tabBar, animated: true, completion: nil)

CPListTemplate

CPListTemplate示例

swift
let item = CPListItem(text: "Favorites", detailText: "12 items")
item.handler = { selectedItem, completion in
    self.interfaceController?.pushTemplate(detailTemplate, animated: true,
                                           completion: nil)
    completion()
}

let section = CPListSection(items: [item], header: "Library",
                            sectionIndexTitle: nil)
let listTemplate = CPListTemplate(title: "My App", sections: [section])
swift
let item = CPListItem(text: "Favorites", detailText: "12 items")
item.handler = { selectedItem, completion in
    self.interfaceController?.pushTemplate(detailTemplate, animated: true,
                                           completion: nil)
    completion()
}

let section = CPListSection(items: [item], header: "Library",
                            sectionIndexTitle: nil)
let listTemplate = CPListTemplate(title: "My App", sections: [section])

Navigation Apps

导航应用

Navigation apps use
com.apple.developer.carplay-maps
. They are the only category that receives a
CPWindow
for drawing map content. The root template must be a
CPMapTemplate
.
导航类应用使用
com.apple.developer.carplay-maps
权限。它们是唯一能获取
CPWindow
用于绘制地图内容的分类。根模板必须是
CPMapTemplate

Trip Preview and Route Selection

行程预览与路线选择

swift
let routeChoice = CPRouteChoice(
    summaryVariants: ["Fastest Route", "Fast"],
    additionalInformationVariants: ["Via Highway 101"],
    selectionSummaryVariants: ["25 min"]
)
let trip = CPTrip(origin: origin, destination: destination,
                  routeChoices: [routeChoice])
mapTemplate.showTripPreviews([trip], textConfiguration: nil)
swift
let routeChoice = CPRouteChoice(
    summaryVariants: ["Fastest Route", "Fast"],
    additionalInformationVariants: ["Via Highway 101"],
    selectionSummaryVariants: ["25 min"]
)
let trip = CPTrip(origin: origin, destination: destination,
                  routeChoices: [routeChoice])
mapTemplate.showTripPreviews([trip], textConfiguration: nil)

Starting a Navigation Session

启动导航会话

swift
extension CarPlaySceneDelegate: CPMapTemplateDelegate {
    func mapTemplate(_ mapTemplate: CPMapTemplate,
                     startedTrip trip: CPTrip,
                     using routeChoice: CPRouteChoice) {
        let session = mapTemplate.startNavigationSession(for: trip)
        session.pauseTrip(for: .loading, description: "Calculating route...")

        let maneuver = CPManeuver()
        maneuver.instructionVariants = ["Turn right onto Main St"]
        maneuver.symbolImage = UIImage(systemName: "arrow.turn.up.right")
        session.upcomingManeuvers = [maneuver]

        let estimates = CPTravelEstimates(
            distanceRemaining: Measurement(value: 5.2, unit: .miles),
            timeRemaining: 900)
        session.updateEstimates(estimates, for: maneuver)
    }
}
swift
extension CarPlaySceneDelegate: CPMapTemplateDelegate {
    func mapTemplate(_ mapTemplate: CPMapTemplate,
                     startedTrip trip: CPTrip,
                     using routeChoice: CPRouteChoice) {
        let session = mapTemplate.startNavigationSession(for: trip)
        session.pauseTrip(for: .loading, description: "Calculating route...")

        let maneuver = CPManeuver()
        maneuver.instructionVariants = ["Turn right onto Main St"]
        maneuver.symbolImage = UIImage(systemName: "arrow.turn.up.right")
        session.upcomingManeuvers = [maneuver]

        let estimates = CPTravelEstimates(
            distanceRemaining: Measurement(value: 5.2, unit: .miles),
            timeRemaining: 900)
        session.updateEstimates(estimates, for: maneuver)
    }
}

Map Buttons

地图按钮

swift
let zoomIn = CPMapButton { _ in self.mapViewController.zoomIn() }
zoomIn.image = UIImage(systemName: "plus.magnifyingglass")
mapTemplate.mapButtons = [zoomIn, zoomOut]
swift
let zoomIn = CPMapButton { _ in self.mapViewController.zoomIn() }
zoomIn.image = UIImage(systemName: "plus.magnifyingglass")
mapTemplate.mapButtons = [zoomIn, zoomOut]

CPSearchTemplate

CPSearchTemplate示例

swift
extension CarPlaySceneDelegate: CPSearchTemplateDelegate {
    func searchTemplate(_ searchTemplate: CPSearchTemplate,
                        updatedSearchText searchText: String,
                        completionHandler: @escaping ([CPListItem]) -> Void) {
        performSearch(query: searchText) { results in
            completionHandler(results.map {
                CPListItem(text: $0.name, detailText: $0.address)
            })
        }
    }

    func searchTemplate(_ searchTemplate: CPSearchTemplate,
                        selectedResult item: CPListItem,
                        completionHandler: @escaping () -> Void) {
        // Navigate to selected destination
        completionHandler()
    }
}
swift
extension CarPlaySceneDelegate: CPSearchTemplateDelegate {
    func searchTemplate(_ searchTemplate: CPSearchTemplate,
                        updatedSearchText searchText: String,
                        completionHandler: @escaping ([CPListItem]) -> Void) {
        performSearch(query: searchText) { results in
            completionHandler(results.map {
                CPListItem(text: $0.name, detailText: $0.address)
            })
        }
    }

    func searchTemplate(_ searchTemplate: CPSearchTemplate,
                        selectedResult item: CPListItem,
                        completionHandler: @escaping () -> Void) {
        // 导航至选中的目的地
        completionHandler()
    }
}

Audio Apps

音频应用

Audio apps use
com.apple.developer.carplay-audio
. They display browsable content in lists and use
CPNowPlayingTemplate
for playback controls.
音频类应用使用
com.apple.developer.carplay-audio
权限。它们在列表中展示可浏览内容,并使用
CPNowPlayingTemplate
提供播放控制。

Now Playing Template

正在播放模板

CPNowPlayingTemplate
is a shared singleton. It reads metadata from
MPNowPlayingInfoCenter
. Do not instantiate a new one.
swift
let nowPlaying = CPNowPlayingTemplate.shared
nowPlaying.isUpNextButtonEnabled = true
nowPlaying.isAlbumArtistButtonEnabled = true
nowPlaying.updateNowPlayingButtons([
    CPNowPlayingShuffleButton { _ in self.toggleShuffle() },
    CPNowPlayingRepeatButton { _ in self.toggleRepeat() }
])
nowPlaying.add(self) // Register as CPNowPlayingTemplateObserver
CPNowPlayingTemplate
是共享单例。它从
MPNowPlayingInfoCenter
读取元数据。请勿实例化新的对象。
swift
let nowPlaying = CPNowPlayingTemplate.shared
nowPlaying.isUpNextButtonEnabled = true
nowPlaying.isAlbumArtistButtonEnabled = true
nowPlaying.updateNowPlayingButtons([
    CPNowPlayingShuffleButton { _ in self.toggleShuffle() },
    CPNowPlayingRepeatButton { _ in self.toggleRepeat() }
])
nowPlaying.add(self) // 注册为CPNowPlayingTemplateObserver

Siri Assistant Cell

Siri助手单元格

Audio apps supporting
INPlayMediaIntent
can show an assistant cell. Communication apps use
INStartCallIntent
with
.startCall
.
swift
let config = CPAssistantCellConfiguration(
    position: .top, visibility: .always, assistantAction: .playMedia)
let listTemplate = CPListTemplate(
    title: "Playlists",
    sections: [CPListSection(items: items)],
    assistantCellConfiguration: config)
支持
INPlayMediaIntent
的音频应用可展示助手单元格。通讯类应用则使用
INStartCallIntent
配合
.startCall
swift
let config = CPAssistantCellConfiguration(
    position: .top, visibility: .always, assistantAction: .playMedia)
let listTemplate = CPListTemplate(
    title: "Playlists",
    sections: [CPListSection(items: items)],
    assistantCellConfiguration: config)

Communication Apps

通讯应用

Communication apps use
com.apple.developer.carplay-communication
. They display message lists and contacts, and support
INStartCallIntent
for Siri-initiated calls.
swift
let message = CPMessageListItem(
    conversationIdentifier: "conv-123",
    text: "Meeting at 3pm",
    leadingConfiguration: CPMessageListItem.LeadingConfiguration(
        leadingItem: .init(text: "Jane", textStyle: .abbreviated),
        unread: true),
    trailingConfiguration: CPMessageListItem.TrailingConfiguration(
        trailingItem: .init(text: "2:45 PM")),
    trailingText: nil, trailingImage: nil)

let messageList = CPListTemplate(title: "Messages",
                                 sections: [CPListSection(items: [message])])
通讯类应用使用
com.apple.developer.carplay-communication
权限。它们展示消息列表和联系人,并支持
INStartCallIntent
以实现Siri发起的通话。
swift
let message = CPMessageListItem(
    conversationIdentifier: "conv-123",
    text: "Meeting at 3pm",
    leadingConfiguration: CPMessageListItem.LeadingConfiguration(
        leadingItem: .init(text: "Jane", textStyle: .abbreviated),
        unread: true),
    trailingConfiguration: CPMessageListItem.TrailingConfiguration(
        trailingItem: .init(text: "2:45 PM")),
    trailingText: nil, trailingImage: nil)

let messageList = CPListTemplate(title: "Messages",
                                 sections: [CPListSection(items: [message])])

Point of Interest Apps

兴趣点应用

EV charging, parking, and food ordering apps use
CPPointOfInterestTemplate
and
CPInformationTemplate
to display locations and details.
电动车充电、停车和点餐类应用使用
CPPointOfInterestTemplate
CPInformationTemplate
展示地点及详情。

CPPointOfInterestTemplate

CPPointOfInterestTemplate示例

swift
let poi = CPPointOfInterest(
    location: MKMapItem(placemark: MKPlacemark(
        coordinate: CLLocationCoordinate2D(latitude: 37.7749,
                                           longitude: -122.4194))),
    title: "SuperCharger Station", subtitle: "4 available",
    summary: "150 kW DC fast charging",
    detailTitle: "SuperCharger Station", detailSubtitle: "$0.28/kWh",
    detailSummary: "Open 24 hours",
    pinImage: UIImage(systemName: "bolt.fill"))

poi.primaryButton = CPTextButton(title: "Navigate",
                                 textStyle: .confirm) { _ in }

let poiTemplate = CPPointOfInterestTemplate(
    title: "Nearby Chargers", pointsOfInterest: [poi], selectedIndex: 0)
poiTemplate.pointOfInterestDelegate = self
swift
let poi = CPPointOfInterest(
    location: MKMapItem(placemark: MKPlacemark(
        coordinate: CLLocationCoordinate2D(latitude: 37.7749,
                                           longitude: -122.4194))),
    title: "SuperCharger Station", subtitle: "4 available",
    summary: "150 kW DC fast charging",
    detailTitle: "SuperCharger Station", detailSubtitle: "$0.28/kWh",
    detailSummary: "Open 24 hours",
    pinImage: UIImage(systemName: "bolt.fill"))

poi.primaryButton = CPTextButton(title: "Navigate",
                                 textStyle: .confirm) { _ in }

let poiTemplate = CPPointOfInterestTemplate(
    title: "Nearby Chargers", pointsOfInterest: [poi], selectedIndex: 0)
poiTemplate.pointOfInterestDelegate = self

CPInformationTemplate

CPInformationTemplate示例

swift
let infoTemplate = CPInformationTemplate(
    title: "Order Summary", layout: .leading,
    items: [
        CPInformationItem(title: "Item", detail: "Burrito Bowl"),
        CPInformationItem(title: "Total", detail: "$12.50")],
    actions: [
        CPTextButton(title: "Place Order", textStyle: .confirm) { _ in
            self.placeOrder() },
        CPTextButton(title: "Cancel", textStyle: .cancel) { _ in
            self.interfaceController?.popTemplate(animated: true,
                                                  completion: nil) }])
swift
let infoTemplate = CPInformationTemplate(
    title: "Order Summary", layout: .leading,
    items: [
        CPInformationItem(title: "Item", detail: "Burrito Bowl"),
        CPInformationItem(title: "Total", detail: "$12.50")],
    actions: [
        CPTextButton(title: "Place Order", textStyle: .confirm) { _ in
            self.placeOrder() },
        CPTextButton(title: "Cancel", textStyle: .cancel) { _ in
            self.interfaceController?.popTemplate(animated: true,
                                                  completion: nil) }])

Testing with CarPlay Simulator

使用CarPlay模拟器测试

  1. Build and run in Xcode with the iOS simulator.
  2. Choose I/O > External Displays > CarPlay.
Default window: 800x480 at @2x. Enable extra options for navigation apps:
bash
defaults write com.apple.iphonesimulator CarPlayExtraOptions -bool YES
  1. 在Xcode中构建并运行iOS模拟器。
  2. 选择I/O > 外部显示器 > CarPlay。
默认窗口:800x480 @2x。为导航应用启用额外选项:
bash
defaults write com.apple.iphonesimulator CarPlayExtraOptions -bool YES

Recommended Test Configurations

推荐测试配置

ConfigurationPixelsScale
Minimum748 x 456@2x
Portrait768 x 1024@2x
Standard800 x 480@2x
High-resolution1920 x 720@3x
Simulator cannot test locked-iPhone behavior, Siri, audio coexistence with car radio, or physical input hardware (knobs, touch pads). Test on a real CarPlay-capable vehicle or aftermarket head unit when possible.
配置像素缩放比
最小尺寸748 x 456@2x
竖屏768 x 1024@2x
标准800 x 480@2x
高分辨率1920 x 720@3x
模拟器无法测试iPhone锁定状态下的行为、Siri、与车载收音机的音频共存或物理输入硬件(旋钮、触控板)。尽可能在支持CarPlay的真实车辆或 aftermarket 主机上测试。

Common Mistakes

常见错误

DON'T: Use the wrong scene delegate method

错误:使用错误的场景代理方法

Navigation apps must implement
templateApplicationScene(_:didConnect:to:)
(with
CPWindow
). Non-navigation apps use
templateApplicationScene(_:didConnect:)
(no window). Using the wrong variant produces no CarPlay UI.
导航类应用必须实现
templateApplicationScene(_:didConnect:to:)
(带
CPWindow
)。非导航类应用使用
templateApplicationScene(_:didConnect:)
(无窗口)。使用错误的方法会导致CarPlay界面无法显示。

DON'T: Draw custom UI in the navigation window

错误:在导航窗口中绘制自定义UI

CPWindow
is exclusively for map content. All overlays, alerts, and controls must use CarPlay templates.
CPWindow
专用于地图内容。所有覆盖层、弹窗和控件必须使用CarPlay模板。

DON'T: Push or present CPTabBarTemplate

错误:推入或模态展示CPTabBarTemplate

CPTabBarTemplate
can only be set as root. Pushing or presenting it fails. Use
setRootTemplate(_:animated:completion:)
.
CPTabBarTemplate
只能设为根模板。推入或模态展示会失败。请使用
setRootTemplate(_:animated:completion:)

DON'T: Instantiate CPNowPlayingTemplate

错误:实例化CPNowPlayingTemplate

Use
CPNowPlayingTemplate.shared
. Creating a new instance causes issues.
请使用
CPNowPlayingTemplate.shared
。创建新实例会导致问题。

DON'T: Ignore vehicle display limits

错误:忽略车载显示屏限制

Check
CPSessionConfiguration.limitedUserInterfaces
and respect
maximumItemCount
/
maximumSectionCount
on list templates.
请检查
CPSessionConfiguration.limitedUserInterfaces
,并遵守列表模板的
maximumItemCount
/
maximumSectionCount
限制。

DON'T: Forget to call the completion handler

错误:忘记调用完成处理函数

CPListItem.handler
must call its completion handler in every code path. Failure leaves the list in a loading state.
CPListItem.handler
必须在所有代码路径中调用其完成处理函数。否则列表会一直处于加载状态。

Review Checklist

审核检查清单

  • Correct CarPlay entitlement key in
    Entitlements.plist
  • UIApplicationSupportsMultipleScenes
    set to
    true
  • CPTemplateApplicationSceneSessionRoleApplication
    scene in Info.plist
  • Scene delegate class name matches
    UISceneDelegateClassName
  • Correct delegate method used (with/without
    CPWindow
    )
  • Root template set in
    didConnect
    before returning
  • Interface controller and window references cleared on disconnect
  • CPTabBarTemplate
    only used as root, never pushed
  • CPNowPlayingTemplate.shared
    used, not a new instance
  • maximumItemCount
    /
    maximumSectionCount
    checked before populating lists
  • CPListItem.handler
    calls completion in every path
  • Map-only content in
    CPWindow
    root view controller (navigation apps)
  • App functions while iPhone is locked
  • Tested at minimum, standard, and high-resolution simulator sizes
  • Audio session deactivated when not actively playing
  • Entitlements.plist
    中使用正确的CarPlay权限密钥
  • UIApplicationSupportsMultipleScenes
    设为
    true
  • Info.plist中包含
    CPTemplateApplicationSceneSessionRoleApplication
    场景
  • 场景代理类名与
    UISceneDelegateClassName
    匹配
  • 使用了正确的代理方法(带/不带
    CPWindow
  • didConnect
    返回前设置了根模板
  • 断开连接时清除了界面控制器和窗口引用
  • CPTabBarTemplate
    仅作为根模板使用,未被推入
  • 使用
    CPNowPlayingTemplate.shared
    而非新实例
  • 填充列表前检查了
    maximumItemCount
    /
    maximumSectionCount
  • CPListItem.handler
    在所有路径中都调用了完成函数
  • CPWindow
    根视图控制器中仅包含地图内容(导航类应用)
  • iPhone锁定时应用仍能正常工作
  • 在最小、标准和高分辨率模拟器尺寸下进行了测试
  • 非活跃播放时停用了音频会话

References

参考资料