WWDC 2018:車載(CarPlay)在音頻和導航 APP 中的應用

WWDC 2018 Session 213: CarPlay Audio and Navigation Appsbash

查看更多 WWDC 18 相關文章請前往 老司機x知識小集xSwiftGG WWDC 18 專題目錄markdown

做者:Lefe_x網絡

CarPlay 從出現至今,逐步開放了它的 API,讓第三方 APP 能夠輕鬆地接入,這樣當你在開車的時候就能夠享受到不同的開車體驗。就在今年(2018),蘋果容許第三方導航 APP 使用 CarPlay,這樣就能夠把第三方的導航數據同步到 CarPlay 上,爲此在 iOS12 中新增了 CarPlay framework,是否是很酷。與此同時蘋果優化了 CarPlay 對 Audio APP 的支持。在這個 session 中,蘋果主要從如下三方面進行講述:session

  • 音頻 APP 中 CarPlay 的性能提高
  • 導航 APP 中新增的 CarPlay 框架
  • Guidance

音頻 APP 中 CarPlay 的性能提高

在開始以前,咱們須要瞭解下 CarPlay 的基本特性,好比觸摸屏、旋轉旋鈕和觸摸板輸入、左右切換按鈕、夜間模式和屏幕大小等。這些基本的特性和咱們使用手機同樣,經過觸摸屏和 CarPlay 進行交互。其實關鍵點是能夠把手機上的數據傳輸到 CarPlay 屏幕上,因爲 CarPlay 提供了模版式的 UI,這樣就不須要適配各類車型。 目前 CarPlay 中主要支持瞭如下功能:app

  • 經過 Automaker 來查看天氣預報,收音機,警告等;
  • 經過 Messaging 進行發送接收消息;
  • 經過 VoIP calling 進行通話;
  • 經過 Audio 來播放音樂聽廣播等;
  • 經過 Navigation 進行導航。

而咱們今天的主角是 Audio 和 Navigation。框架

關於 CarPlay 中的音頻 APP,官方以一個名叫 Srirocka 的 APP 來進行講解。在開發一款音頻 APP 時,咱們可讓它支持 CarPlay,只須要使用 MediaPlayer 這個框架中的 API 便可,它能夠在全部的 CarPlay 系統中良好的運行,咱們只須要提供給 CarPlay 須要的數據便可。ide

CarPlay 中主要的 API 有三部分,如圖所示。MPPlayableContent 負責展現內容,好比播放某個專輯須要顯示專輯中的音頻列表。顯示的數據須要經過 MPPlayableContentManager 來設置他的 dataSource 和 delegate,而這些和 UITableView 很是像;MPNowPlayingInfoCenter 負責顯示正在播放的音頻信息,好比音頻名字,做者,時長等信息;MPRemoteCommandCenter 指令中心,負責接收指令,好比在 CarPlay 中點擊了暫停按鈕,那麼在手機 APP 中須要執行暫停操做。oop

具體代碼能夠參考:性能

// 設置 MPPlayableContentManager 的 data source 和 delegate,爲 
// MPPlayableContentManager 提供數據
[MPPlayableContentManager sharedContentManager].dataSource = self;
[MPPlayableContentManager sharedContentManager].delegate = self;
    
// 經過 MPNowPlayingInfoCenter 設置正在播放的音樂的基本信息
[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = @{ MPMediaItemPropertyAlbumTitle : @"草原"};
    
// 響應遠程指令
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
commandCenter.playCommand.enabled = YES;
[commandCenter.playCommand addTarget:self action:@selector(playCommendActuon)];
複製代碼

以上這些 API 在 iOS12 以前已經提供給了開發者,而在 iOS12 中主要是作了一些優化:學習

  • 提高了 MPPlayableContent 的性能;
  • 提高了啓動速度;
  • 提高了動畫流暢度;
  • 提高了 CarPlay 與 APP 的交互性。

蘋果建議開發者僅僅在須要的時候調用 MPPlayableContentManagerreloadData 方法,以及可使用 beginUpdatesendUpdates 來同時刷新多個數據。

蘋果特別強調,開發者須要注意的一些場景,好比鏈接到 CarPlay 的 iPhone 被密碼鎖定,弱網狀況等。開發者須要預處理一些要播放的內容,好比當要播放下一首音樂的時候,須要預先加載,這樣能夠有效避免網絡異常時出現的錯誤。能夠在下面這個方法來處理預先加載的音頻。

- (void)beginLoadingChildItemsAtIndexPath:(NSIndexPath *)indexPath completionHandler:(void (^)(NSError * _Nullable))completionHandler
{
    completionHandler(nil);
}
複製代碼

導航 APP 中新增的 CarPlay 框架

在蘋果今年發佈的 iOS12 中,CarPlay 能夠支持第三方導航,好比谷歌地圖。而在 Xcode 10 中能夠找到 CarPlay Framework。你能夠在導航 APP 中自定義界面來顯示導航信息。蘋果提供了不少模版來顯示不一樣的視圖,開發者須要作的就是把須要展現的數據交給模版,這樣 CarPlay 就會顯示你所定義的數據。這種靈活性可讓你專一於 CarPlay 的體驗而不須要花費力氣適配全部支持 CarPlay 的車型。你只需花費一點點精力便可讓你的導航 APP 擁有 CarPlay 能力。

在學習 CarPlay 框架的時候,咱們須要瞭解這個框架中各個類的做用,爲方便理解,小編(@Lefe_x)作了一個圖:

其實看完上面的圖,咱們能夠知道 CarPlay 這個框架所作的事就是顯示 UI 到 CarPlay 屏幕上,而這些 UI 都提供了對應的模版,只要按着這些模版建立不一樣的 UI 便可適配全部的車型。

咱們下面跟着代碼來看看 CarPlay 的使用:

首先須要實現 CPApplicationDelegate,這個代理主要用來監聽與 CarPlay 鏈接成功和斷開鏈接;監聽用戶點擊 Alert 的事件。

而這一切須要 CPMapTemplate 來承載整個地圖界面,它是一個根模版。CPMapTemplate 的做用能夠用來管理 Pin 手勢,顯示導航提示,顯示導航條或地圖按鈕。以下圖所示:

// CarPlay 與 APP 鏈接成功後的回調
- (void)application:(nonnull UIApplication *)application didConnectCarInterfaceController:(nonnull CPInterfaceController *)interfaceController toWindow:(nonnull UIWindow *)window {
    
    self.interfaceController = interfaceController;
    self.carWindow = window;
    
    UIViewController *rootVC = [UIViewController new];
    window.rootViewController = rootVC;
    
    // 建立 rootTemplate,CPMapTemplate 主要用來處理手勢,顯示導航提示,引導
    CPMapTemplate *rootTemplate = [self createRootTemplate];
    [self.interfaceController setRootTemplate:rootTemplate animated:NO];
}

// CarPlay 與 APP 端開連接後的回調
- (void)application:(nonnull UIApplication *)application didDisconnectCarInterfaceController:(nonnull CPInterfaceController *)interfaceController fromWindow:(nonnull UIWindow *)window {
    
}

// 建立 CPMapTemplate
- (CPMapTemplate *)createRootTemplate
{
    CPMapTemplate *template = [[CPMapTemplate alloc] init];
    CPBarButton *categorySearchButton = [[CPBarButton alloc] initWithType:CPBarButtonTypeImage handler:^(CPBarButton * _Nonnull barButton) {
        [self displayFavoriteCategories];
    }];
    categorySearchButton.image = [UIImage imageNamed:@"Favorites"];
    
    CPBarButton *trafficButton = [[CPBarButton alloc] initWithType:CPBarButtonTypeImage handler:^(CPBarButton * _Nonnull barButton) {
        ;
    }];
    trafficButton.image = [UIImage imageNamed:@"traffic"];
    // 導航上添加了兩個按鈕
    template.trailingNavigationBarButtons = @[trafficButton, categorySearchButton];
    return template;
}
複製代碼

map-templeate.png

CPGridTemplate 是一個網狀的模版,相似於 UICollectionView。最多隻顯示 8 個按鈕,它適合顯示多行多列的菜單。

// 建立 CPGridTemplate,它會有多個 CPGridButton
- (void)displayFavoriteCategories
{
    CPGridButton *parksButton = [[CPGridButton alloc] initWithTitleVariants:@[@"Parks"] image:[UIImage imageNamed:@"Parks"] handler:^(CPGridButton * _Nonnull barButton) {
        [self searchForNearbyParks];
    }];
    
    CPGridButton *beachesButton = [[CPGridButton alloc] initWithTitleVariants:@[@"beaches"] image:[UIImage imageNamed:@"beaches"] handler:^(CPGridButton * _Nonnull barButton) {
        
    }];
    
    CPGridButton *forestsButton = [[CPGridButton alloc] initWithTitleVariants:@[@"forests"] image:[UIImage imageNamed:@"forests"] handler:^(CPGridButton * _Nonnull barButton) {
        
    }];
    
    CPGridButton *desertsButton = [[CPGridButton alloc] initWithTitleVariants:@[@"deserts"] image:[UIImage imageNamed:@"deserts"] handler:^(CPGridButton * _Nonnull barButton) {
        
    }];
    
    NSArray *buttons = @[parksButton, beachesButton, forestsButton, desertsButton];
    
    CPGridTemplate *template = [[CPGridTemplate alloc] initWithTitle:@"Favorites" gridButtons:buttons];
    
    [self.interfaceController pushTemplate:template animated:YES];
}
複製代碼

CPListTemplate 和 UITableView 很類似,它由一個或多個分組組成,而每個分組使用 CPListSection 表示,CPListSection中存放的是 CPListItem,能夠設置它的標題,圖片,副標題。它適合用於顯示列表類型的 UI,猶如 UITableView

- (void)displayNearbyParks:(NSArray<SearchResult *> *)results
{
    NSMutableArray *listItems = [NSMutableArray array];
    for (int i = 0; i < results.count; i++) {
        SearchResult *item = results[i];
        [listItems addObject:[[CPListItem alloc] initWithText:item.name detailText:item.address image:item.image]];
    }
    
    CPListSection *section = [[CPListSection alloc] initWithItems:listItems];
    CPListTemplate *listTemplate = [[CPListTemplate alloc] initWithSections:@[section]];
    listTemplate.title = @"Parks";
    listTemplate.delegate = self;
    
    [self.interfaceController pushTemplate:listTemplate animated:YES];
}
複製代碼

CPSearchTemplate 用來搜索目的地,並顯示搜索結果。

CPVoiceControlTemplate 一個語音控制的模版,用於語音搜索樣式。

Guidance

當用戶選擇一個目的地開啓導航後,會彈出一個導航預覽,用戶確認後開始導航,直到導航結束。下面這張圖就是整個導航的流程:

當用戶選擇好目的地後,這時 CarPlay 會顯示一個預覽,用戶若是點擊「肯定」,導航便開始。具體能夠查看代碼:

導航開始執行後,下面這個代理方法會執行。咱們須要在 CPMapTemplateDelegate 中處理。這個時候須要開啓 CPNavigationSession 它表示一次導航會話,能夠經過 CPNavigationSession 對此次的導航進行操做,好比取消本次導航。

- (void)mapTemplate:(CPMapTemplate *)mapTemplate startedTrip:(CPTrip *)trip usingRouteChoice:(CPRouteChoice *)routeChoice
{
    [mapTemplate hideTripPreviews];
    
    // 當用戶選擇一個路線後將執行
    CPNavigationSession *session = [mapTemplate startNavigationSessionForTrip:trip];
    [session pauseTripForReason:CPTripPauseReasonLoading];
}
複製代碼

能夠經過 CPMapTemplateDelegate 代理方法來控制顯示當前導航的狀態。

- (BOOL)mapTemplate:(CPMapTemplate *)mapTemplate shouldShowNotificationForManeuver:(CPManeuver *)maneuver {return YES;}

- (BOOL)mapTemplate:(CPMapTemplate *)mapTemplate shouldUpdateNotificationForManeuver:(CPManeuver *)maneuver withTravelEstimates:(CPTravelEstimates *)travelEstimates { return YES;}

- (BOOL)mapTemplate:(CPMapTemplate *)mapTemplate shouldShowNotificationForNavigationAlert:(CPNavigationAlert *)navigationAlert{
    return YES; }
複製代碼

總結

總的來講,CarPlay 今年最大的改動就是能夠在導航 APP 中使用 CarPlay,提高了 Audio APP 在 CarPlay 中的性能。若是你正在作導航類型的 APP,能夠嘗試支持 CarPlay。

參考

相關文章
相關標籤/搜索