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

知識小集發表於2018-06-11

WWDC 2018 Session 213: CarPlay Audio and Navigation Apps

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

作者:Lefe_x

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

  • 音訊 APP 中 CarPlay 的效能提升
  • 導航 APP 中新增的 CarPlay 框架
  • Guidance

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

音訊 APP 中 CarPlay 的效能提升

在開始之前,我們需要了解下 CarPlay 的基本特性,比如觸控式螢幕、旋轉旋鈕和觸控板輸入、左右切換按鈕、夜間模式和螢幕大小等。這些基本的特性和我們使用手機一樣,通過觸控式螢幕和 CarPlay 進行互動。其實關鍵點是可以把手機上的資料傳輸到 CarPlay 螢幕上,由於 CarPlay 提供了模版式的 UI,這樣就不需要適配各種車型。 目前 CarPlay 中主要支援了以下功能:

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

而我們今天的主角是 Audio 和 Navigation。

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

關於 CarPlay 中的音訊 APP,官方以一個名叫 Srirocka 的 APP 來進行講解。在開發一款音訊 APP 時,我們可以讓它支援 CarPlay,只需要使用 MediaPlayer 這個框架中的 API 即可,它可以在所有的 CarPlay 系統中良好的執行,我們只需要提供給 CarPlay 需要的資料即可。

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

CarPlay 中主要的 API 有三部分,如圖所示。MPPlayableContent 負責展示內容,比如播放某個專輯需要顯示專輯中的音訊列表。顯示的資料需要通過 MPPlayableContentManager 來設定他的 dataSource 和 delegate,而這些和 UITableView 非常像;MPNowPlayingInfoCenter 負責顯示正在播放的音訊資訊,比如音訊名字,作者,時長等資訊;MPRemoteCommandCenter 指令中心,負責接收指令,比如在 CarPlay 中點選了暫停按鈕,那麼在手機 APP 中需要執行暫停操作。

具體程式碼可以參考:

// 設定 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 框架

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

在蘋果今年釋出的 iOS12 中,CarPlay 可以支援第三方導航,比如谷歌地圖。而在 Xcode 10 中可以找到 CarPlay Framework。你可以在導航 APP 中自定義介面來顯示導航資訊。蘋果提供了很多模版來顯示不同的檢視,開發者需要做的就是把需要展示的資料交給模版,這樣 CarPlay 就會顯示你所定義的資料。這種靈活性可以讓你專注於 CarPlay 的體驗而不需要花費力氣適配所有支援 CarPlay 的車型。你只需花費一點點精力即可讓你的導航 APP 擁有 CarPlay 能力。

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

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

其實看完上面的圖,我們可以知道 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];
}
複製程式碼

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

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];
}
複製程式碼

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

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

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

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

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

Guidance

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

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

當使用者選擇好目的地後,這時 CarPlay 會顯示一個預覽,使用者如果點選“確定”,導航便開始。具體可以檢視程式碼:

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

導航開始執行後,下面這個代理方法會執行。我們需要在 CPMapTemplateDelegate 中處理。這個時候需要開啟 CPNavigationSession 它表示一次導航會話,可以通過 CPNavigationSession 對這次的導航進行操作,比如取消本次導航。

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

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

可以通過 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。

參考

相關文章