自定義 long-look 通知界面包含兩個獨立部分,一個是靜態(tài)的,一個是動態(tài)的。靜態(tài)的界面是必須的,并簡單的顯示通知的提示信息,它的圖片和文字是在設(shè)計的時候就配置的。動態(tài)界面是可選的,只是為了讓你有方法能自定義顯示通知內(nèi)容。
當(dāng)你在 storyboard 中創(chuàng)建一個新的通知界面 controller 的時候,Xcode 只會默認(rèn)創(chuàng)建一個靜態(tài)界面,如果你需要創(chuàng)建動態(tài)界面,需要在 storyboard 的通知目錄對象中選擇 “Has Dynamic Interface” 屬性。(只有在靜態(tài)界面無法滿足你需要的時,再選擇添加動態(tài)界面才是明智的選擇)。圖 16-1 顯示了 storyboard 文件中未修改的靜態(tài)和動態(tài)界面場景。靜態(tài)和動態(tài)場景都是同一類型的相關(guān)通知,你可以使用通知 category 對象附加到靜態(tài)場景來配置。
圖 16-1 靜態(tài)和動態(tài)的通知界面
http://wiki.jikexueyuan.com/project/apple-watch-programming-guide/images/notification_static_dynamic_2x.png" alt="靜態(tài)和動態(tài)的通知界面" />
當(dāng)合法的通知類型被接受后,WatchKit 會基于不同的因素來選擇靜態(tài)或者動態(tài)界面。當(dāng)動態(tài)頁面不存在,或者當(dāng)電量不夠,又或者你根本就沒有明確告訴 WatchKit 顯示動態(tài)界面的時候,WatchKit 會自動顯示靜態(tài)頁面。在其他情況下,WatchKit 會顯示你的動態(tài)頁面。在確定顯示動態(tài)頁面后,WatchKit 會加載合適的 storyboard 資源然后準(zhǔn)備合適的接口。就如圖 16-2 中所描述。 動態(tài)界面的加載過程就跟你應(yīng)用程序中的界面 controller 一樣,只是這個是通知界面 controller 特定的用來處理通知負(fù)載數(shù)據(jù)的。
圖 16-2 準(zhǔn)備通知界面
http://wiki.jikexueyuan.com/project/apple-watch-programming-guide/images/notification_process_2x.png" alt="準(zhǔn)備通知界面" />
當(dāng)你為你的目標(biāo)應(yīng)用程序創(chuàng)建 WatchKit 程序時,你需要勾選上 Include Notification Scene 選項來創(chuàng)建對應(yīng)通知接口的實現(xiàn)文件。Xcode 提供了一個空的 storyboard 場景和一個用于你通知接口 controller 的自定義子類。如果你在創(chuàng)建的時候沒有選中剛才說的那個選項,而你又需要創(chuàng)建通知接口,則需要手動自己添加。
手動添加通知接口:拖一個通知接口 controller 對象到你的 storyboard 文件。新的接口僅僅只是包含了靜態(tài)的頁面控制器,為了創(chuàng)建動態(tài)接口,你必須執(zhí)行以下配置步驟:
配置動態(tài)通知接口controller
在你的項目中,創(chuàng)建一個新的 WKUserNotificationInterfaceController子類。
創(chuàng)建新的資源文件并將其添加到你的 WatchKit 拓展目標(biāo),然后給你的類一個合適的名字以區(qū)別于其他通知接口 controller。
勾選通知目錄中得 Has Dynamic Interface,這一步將添加動態(tài)場景到你的 storyboard 文件。
應(yīng)用程序可能有多個通知界面,你可以使用目錄(categories)來區(qū)分。再你得 storyboard 文件中,使用 Notification Category 對象指定 category 名字來關(guān)聯(lián)不同的場景。WatchKit 使用 category 值來決定在運行時加載哪個場景。如果進來的通知沒有 category,WatchKit 則加載 category 名為 default 的。更多關(guān)于如何指定通知的 category,請見 “Configuring the Category of a Custom Interface”。
每個通知接口必須有指定的通知 category,這樣才能告訴 Apple Watch 什么時候使用。發(fā)送進來的通知可以在負(fù)載數(shù)據(jù)中包含一個 category 鍵,值則你自己定義。Apple Watch 使用 category 來決定顯示哪個通知場景。如果進來的通知沒有包含 category 字符串,Apple Watch 顯示配置時的默認(rèn)通知界面。
指定你通知接口的通知類型,在你的 storyboard 中選擇 “Notification Category” 對象并選擇 Attributes 檢查框,如圖 16-3 所示。在檢查框中得 Name 字段里輸入 category 的名字。同時你也能在這個框中設(shè)置你自定義界面的 sash 顏色和標(biāo)題文字。
圖 16-3 配置通知類型信息
http://wiki.jikexueyuan.com/project/apple-watch-programming-guide/images/notification_type_config_2x.png" alt="配置通知類型信息" />
當(dāng)生成遠程通知時,你的服務(wù)器通過在負(fù)載數(shù)據(jù)的 aps 字典里包含一個 category 鍵來指定通知類型。對于本地通知,你可以通過 UILocalNotification 對象的 category 屬性的值來指定。
注意
category 字符串同樣也能定義哪些活動按鈕(如果有的話)被追加到通知界面的最后,獲取更多關(guān)于支持自定義活動的信息,請見 Adding Action Buttons to Notifications。
使用靜態(tài)通知界面來定義一個簡單的自定義的通知界面,靜態(tài)界面的目的是為了在你 WatchKit 拓展的事件中提供一個回調(diào)接口,WatchKit 拓展是無法及時配置動態(tài)界面的,以下是創(chuàng)建一個靜態(tài)界面的規(guī)則:
所欲的圖像必須位于 WatchKit 應(yīng)用程序包。
界面不能包含控件,表格,地圖或者其他互動元素。
notificationAlertLabel 出口必須連接到一個 label,這個 label 的內(nèi)容設(shè)置為通知的提示信息。對于所有其他標(biāo)簽的文字都不要改變,而要再你的 storyboard 文件中指定。 圖 16-4 展示了在一個日歷程序中自定義通知界面的靜態(tài)和動態(tài)場景。通知箭頭指向了靜態(tài)場景,這個場景中包含了一個自定義 icon 和兩個label。在靜態(tài)界面中,label 包含了一個字符串 <message>,它就是跟 notificationAlertLabel 出口相關(guān)的,因此它會在運行時收到通知的提示消息。
圖 16-4 一個單一通知類型的靜態(tài)和動態(tài)場景
http://wiki.jikexueyuan.com/project/apple-watch-programming-guide/images/notification_design_2x.png" alt="一個單一通知類型的靜態(tài)和動態(tài)場景" />
一個動態(tài)通知界面可以為用戶提供更加豐富的體驗。使用動態(tài)界面,你可以顯示更多而不僅僅只是提示消息。你可以放置更多的信息,配置更多的 label,顯示動態(tài)生成的內(nèi)容等等。
實現(xiàn)動態(tài)的通知界面需要創(chuàng)建一個 WKUserNotificationInterfaceController 的子類。當(dāng)一個合適類型的通知過來時,這個子類的實現(xiàn)就是用來響應(yīng)它的。
配置你得動態(tài)界面就像其他界面 controller 場景,包括在你的子類中引用的 label,image 和其他場景中的對象。然后使用這些在運行時配置場景內(nèi)容。點擊通知頁面啟動應(yīng)用程序,所以通知界面不應(yīng)該包含交互控制。
多為你的界面使用 label,image,group 和 separator(分離器)。
你可以可以在你的界面中包含 table 和 map。
當(dāng)一個合適類型的通知到達時,WatchKit 從你的 storyboard 中選擇合適的場景顯示并告訴你的 WatchKit 拓展初始化對應(yīng) WKUserNotificationInterfaceController 子類。圖 16-5 展示了 WatchKit 需要準(zhǔn)備你界面的步驟,在初始化通知界面 controller 之后,WatchKit 使用 didReceiveRemoteNotification:withCompletion: 或者 didReceiveLocalNotification:withCompletion: 方法來發(fā)送有效負(fù)載數(shù)據(jù),然后你使用負(fù)載數(shù)據(jù)來配置你通知界面的其他部分,然后調(diào)用設(shè)置完成處理程序塊讓 WatchKit 知道你的界面已經(jīng)準(zhǔn)備好了。
圖 16-5 配置動態(tài)通知界面
http://wiki.jikexueyuan.com/project/apple-watch-programming-guide/images/notification_event_cycle_2x.png" alt="配置動態(tài)通知界面" />
總是使用 didReceiveRemoteNotification:withCompletion: 或者 didReceiveLocalNotification:withCompletion: 方法來配置你的通知界面,當(dāng)實現(xiàn)了其中任意方法后,已經(jīng)配置的界面提供的處理程序會盡快執(zhí)行完成。如果等得太久,Apple Watch 會嘗試放棄顯示你的動態(tài)界面而以顯示靜態(tài)界面來代替。
清單 16-1 展示了 didReceiveRemoteNotification:withCompletion: 方法實現(xiàn)的示例。這個方法是通過一個虛構(gòu)的日歷程序,發(fā)送一個新的會議邀請遠程通知。該方法從遠程通知的負(fù)載數(shù)據(jù)中提取數(shù)據(jù)并利用該數(shù)據(jù)來設(shè)置通知界面上的 label 值。為簡單起見,該示例假定服務(wù)器總是為每個 key 都包含了一個合適的值。但你自己的代碼應(yīng)該做一些必要的錯誤檢查以保證這些負(fù)載數(shù)據(jù)是有效的。在配置完 label 之后,則調(diào)用處理程序讓 WatchKit 知道自定義的界面以及準(zhǔn)備好可以顯示了。
清單 16-1 在一個遠程通知里配置自定義界面
// Standard remote notification payload keys.
NSString* apsKeyString = @"aps";
NSString* titleKeyString = @"title";
// Payload keys that are specific to the app.
NSString* customDataKey = @"cal";
NSString* invitationDateKey = @"date";
NSString* invitationLocationKey = @"loc";
NSString* invitationNotesKey = @"note";
- (void)didReceiveRemoteNotification:(NSDictionary *)remoteNotification withCompletion:(void(^)(WKUserNotificationInterfaceType interface)) completionHandler {
// Get the aps dictionary from the payload.
NSDictionary* apsDict = [remoteNotification objectForKey:apsKeyString];
// Retrieve the title of the invitation.
NSString* titleString = [apsDict objectForKey:titleKeyString];
[self.titleLabel setText:titleString];
// Extract the date and time from the custom section of the payload.
// The date/time information is stored as the number of seconds since 1970.
NSDictionary* customDataDict = [remoteNotification objectForKey:customDataKey];
NSNumber* dateValue = [customDataDict objectForKey:invitationDateKey];
NSDate* inviteDate = [NSDate dateWithTimeIntervalSince1970:[dateValue doubleValue]];
// Format the date and time strings.
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
// Call a custom method to get the localized date format string for the user.
// The default date format string is "EEE, MMM d".
dateFormatter.dateFormat = [self dateFormatForCurrentUser];
NSString *formattedDateString = [dateFormatter stringFromDate:inviteDate];
// Call a custom method to get the localized time format string for the user.
// The default time format string is "h:mm a".
dateFormatter.dateFormat = [self timeFormatForCurrentUser];
NSString *formattedTimeString = [dateFormatter stringFromDate:inviteDate];
// Set the date and time in the corresponding labels.
[self.dateLabel setText:formattedDateString];
[self.timeLabel setText:formattedTimeString];
// Set the location of the meeting.
NSString* locationString = [customDataDict objectForKey:invitationLocationKey];
[self.locationLabel setText:locationString];
// Set the invitation's notes (if any).
NSString* notesString = [customDataDict objectForKey:invitationNotesKey];
[self.notesLabel setText:notesString];
// Tell WatchKit to display the custom interface.
completionHandler(WKUserNotificationInterfaceTypeCustom);
}
在調(diào)用完成處理代碼塊時,如果你希望 WatchKit 顯示靜態(tài)界面,那就指定 WKUserNotificationInterfaceTypeDefault 常量。
注意
通知界面的文字僅僅只支持系統(tǒng)指定的文字字體,如果你需要顯示其他字體,最好是將文字嵌入圖片中,然后顯示那張圖片。
當(dāng)你已經(jīng)準(zhǔn)備好在模擬器上測試你的動態(tài)界面時,為你的通知界面創(chuàng)建一個自定義運行計劃,如果你還沒有這么做的話。當(dāng)你配置界面時,指定一個你希望發(fā)送到你的界面并包含測試數(shù)據(jù)的 JSON 文件。Xcode 會為指定的數(shù)據(jù)提供自定義的 JSON 文件。
更多關(guān)于設(shè)置運行計劃并配置你的數(shù)據(jù)的信息,請見 The Build, Run, Debug Process。