自定義 long-look 通知界面包含兩個(gè)獨(dú)立部分,一個(gè)是靜態(tài)的,一個(gè)是動(dòng)態(tài)的。靜態(tài)的界面是必須的,并簡(jiǎn)單的顯示通知的提示信息,它的圖片和文字是在設(shè)計(jì)的時(shí)候就配置的。動(dòng)態(tài)界面是可選的,只是為了讓你有方法能自定義顯示通知內(nèi)容。
當(dāng)你在 storyboard 中創(chuàng)建一個(gè)新的通知界面 controller 的時(shí)候,Xcode 只會(huì)默認(rèn)創(chuàng)建一個(gè)靜態(tài)界面,如果你需要?jiǎng)?chuàng)建動(dòng)態(tài)界面,需要在 storyboard 的通知目錄對(duì)象中選擇 “Has Dynamic Interface” 屬性。(只有在靜態(tài)界面無(wú)法滿足你需要的時(shí),再選擇添加動(dòng)態(tài)界面才是明智的選擇)。圖 16-1 顯示了 storyboard 文件中未修改的靜態(tài)和動(dòng)態(tài)界面場(chǎng)景。靜態(tài)和動(dòng)態(tài)場(chǎng)景都是同一類型的相關(guān)通知,你可以使用通知 category 對(duì)象附加到靜態(tài)場(chǎng)景來(lái)配置。
圖 16-1 靜態(tài)和動(dòng)態(tài)的通知界面
http://wiki.jikexueyuan.com/project/apple-watch-programming-guide/images/notification_static_dynamic_2x.png" alt="靜態(tài)和動(dòng)態(tài)的通知界面" />
當(dāng)合法的通知類型被接受后,WatchKit 會(huì)基于不同的因素來(lái)選擇靜態(tài)或者動(dòng)態(tài)界面。當(dāng)動(dòng)態(tài)頁(yè)面不存在,或者當(dāng)電量不夠,又或者你根本就沒(méi)有明確告訴 WatchKit 顯示動(dòng)態(tài)界面的時(shí)候,WatchKit 會(huì)自動(dòng)顯示靜態(tài)頁(yè)面。在其他情況下,WatchKit 會(huì)顯示你的動(dòng)態(tài)頁(yè)面。在確定顯示動(dòng)態(tài)頁(yè)面后,WatchKit 會(huì)加載合適的 storyboard 資源然后準(zhǔn)備合適的接口。就如圖 16-2 中所描述。 動(dòng)態(tài)界面的加載過(guò)程就跟你應(yīng)用程序中的界面 controller 一樣,只是這個(gè)是通知界面 controller 特定的用來(lái)處理通知負(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 程序時(shí),你需要勾選上 Include Notification Scene 選項(xiàng)來(lái)創(chuàng)建對(duì)應(yīng)通知接口的實(shí)現(xiàn)文件。Xcode 提供了一個(gè)空的 storyboard 場(chǎng)景和一個(gè)用于你通知接口 controller 的自定義子類。如果你在創(chuàng)建的時(shí)候沒(méi)有選中剛才說(shuō)的那個(gè)選項(xiàng),而你又需要?jiǎng)?chuàng)建通知接口,則需要手動(dòng)自己添加。
手動(dòng)添加通知接口:拖一個(gè)通知接口 controller 對(duì)象到你的 storyboard 文件。新的接口僅僅只是包含了靜態(tài)的頁(yè)面控制器,為了創(chuàng)建動(dòng)態(tài)接口,你必須執(zhí)行以下配置步驟:
配置動(dòng)態(tài)通知接口controller
在你的項(xiàng)目中,創(chuàng)建一個(gè)新的 WKUserNotificationInterfaceController子類。
創(chuàng)建新的資源文件并將其添加到你的 WatchKit 拓展目標(biāo),然后給你的類一個(gè)合適的名字以區(qū)別于其他通知接口 controller。
勾選通知目錄中得 Has Dynamic Interface,這一步將添加動(dòng)態(tài)場(chǎng)景到你的 storyboard 文件。
應(yīng)用程序可能有多個(gè)通知界面,你可以使用目錄(categories)來(lái)區(qū)分。再你得 storyboard 文件中,使用 Notification Category 對(duì)象指定 category
名字來(lái)關(guān)聯(lián)不同的場(chǎng)景。WatchKit 使用 category 值來(lái)決定在運(yùn)行時(shí)加載哪個(gè)場(chǎng)景。如果進(jìn)來(lái)的通知沒(méi)有 category,WatchKit 則加載 category 名為 default
的。更多關(guān)于如何指定通知的 category,請(qǐng)見(jiàn) “Configuring the Category of a Custom Interface”。
每個(gè)通知接口必須有指定的通知 category,這樣才能告訴 Apple Watch 什么時(shí)候使用。發(fā)送進(jìn)來(lái)的通知可以在負(fù)載數(shù)據(jù)中包含一個(gè) category 鍵,值則你自己定義。Apple Watch 使用 category 來(lái)決定顯示哪個(gè)通知場(chǎng)景。如果進(jìn)來(lái)的通知沒(méi)有包含 category 字符串,Apple Watch 顯示配置時(shí)的默認(rèn)通知界面。
指定你通知接口的通知類型,在你的 storyboard 中選擇 “Notification Category” 對(duì)象并選擇 Attributes 檢查框,如圖 16-3 所示。在檢查框中得 Name 字段里輸入 category 的名字。同時(shí)你也能在這個(gè)框中設(shè)置你自定義界面的 sash 顏色和標(biāo)題文字。
圖 16-3 配置通知類型信息
http://wiki.jikexueyuan.com/project/apple-watch-programming-guide/images/notification_type_config_2x.png" alt="配置通知類型信息" />
當(dāng)生成遠(yuǎn)程通知時(shí),你的服務(wù)器通過(guò)在負(fù)載數(shù)據(jù)的 aps
字典里包含一個(gè) category
鍵來(lái)指定通知類型。對(duì)于本地通知,你可以通過(guò) UILocalNotification
對(duì)象的 category 屬性的值來(lái)指定。
注意
category 字符串同樣也能定義哪些活動(dòng)按鈕(如果有的話)被追加到通知界面的最后,獲取更多關(guān)于支持自定義活動(dòng)的信息,請(qǐng)見(jiàn) Adding Action Buttons to Notifications。
使用靜態(tài)通知界面來(lái)定義一個(gè)簡(jiǎn)單的自定義的通知界面,靜態(tài)界面的目的是為了在你 WatchKit 拓展的事件中提供一個(gè)回調(diào)接口,WatchKit 拓展是無(wú)法及時(shí)配置動(dòng)態(tài)界面的,以下是創(chuàng)建一個(gè)靜態(tài)界面的規(guī)則:
所欲的圖像必須位于 WatchKit 應(yīng)用程序包。
界面不能包含控件,表格,地圖或者其他互動(dòng)元素。
notificationAlertLabel
出口必須連接到一個(gè) label,這個(gè) label 的內(nèi)容設(shè)置為通知的提示信息。對(duì)于所有其他標(biāo)簽的文字都不要改變,而要再你的 storyboard 文件中指定。 圖 16-4 展示了在一個(gè)日歷程序中自定義通知界面的靜態(tài)和動(dòng)態(tài)場(chǎng)景。通知箭頭指向了靜態(tài)場(chǎng)景,這個(gè)場(chǎng)景中包含了一個(gè)自定義 icon 和兩個(gè)label。在靜態(tài)界面中,label 包含了一個(gè)字符串 <message>
,它就是跟 notificationAlertLabel
出口相關(guān)的,因此它會(huì)在運(yùn)行時(shí)收到通知的提示消息。
圖 16-4 一個(gè)單一通知類型的靜態(tài)和動(dòng)態(tài)場(chǎng)景
http://wiki.jikexueyuan.com/project/apple-watch-programming-guide/images/notification_design_2x.png" alt="一個(gè)單一通知類型的靜態(tài)和動(dòng)態(tài)場(chǎng)景" />
一個(gè)動(dòng)態(tài)通知界面可以為用戶提供更加豐富的體驗(yàn)。使用動(dòng)態(tài)界面,你可以顯示更多而不僅僅只是提示消息。你可以放置更多的信息,配置更多的 label,顯示動(dòng)態(tài)生成的內(nèi)容等等。
實(shí)現(xiàn)動(dòng)態(tài)的通知界面需要?jiǎng)?chuàng)建一個(gè) WKUserNotificationInterfaceController 的子類。當(dāng)一個(gè)合適類型的通知過(guò)來(lái)時(shí),這個(gè)子類的實(shí)現(xiàn)就是用來(lái)響應(yīng)它的。
配置你得動(dòng)態(tài)界面就像其他界面 controller 場(chǎng)景,包括在你的子類中引用的 label,image 和其他場(chǎng)景中的對(duì)象。然后使用這些在運(yùn)行時(shí)配置場(chǎng)景內(nèi)容。點(diǎn)擊通知頁(yè)面啟動(dòng)應(yīng)用程序,所以通知界面不應(yīng)該包含交互控制。
多為你的界面使用 label,image,group 和 separator(分離器)。
你可以可以在你的界面中包含 table 和 map。
當(dāng)一個(gè)合適類型的通知到達(dá)時(shí),WatchKit 從你的 storyboard 中選擇合適的場(chǎng)景顯示并告訴你的 WatchKit 拓展初始化對(duì)應(yīng) WKUserNotificationInterfaceController 子類。圖 16-5 展示了 WatchKit 需要準(zhǔn)備你界面的步驟,在初始化通知界面 controller 之后,WatchKit 使用 didReceiveRemoteNotification:withCompletion: 或者 didReceiveLocalNotification:withCompletion: 方法來(lái)發(fā)送有效負(fù)載數(shù)據(jù),然后你使用負(fù)載數(shù)據(jù)來(lái)配置你通知界面的其他部分,然后調(diào)用設(shè)置完成處理程序塊讓 WatchKit 知道你的界面已經(jīng)準(zhǔn)備好了。
圖 16-5 配置動(dòng)態(tài)通知界面
http://wiki.jikexueyuan.com/project/apple-watch-programming-guide/images/notification_event_cycle_2x.png" alt="配置動(dòng)態(tài)通知界面" />
總是使用 didReceiveRemoteNotification:withCompletion: 或者 didReceiveLocalNotification:withCompletion: 方法來(lái)配置你的通知界面,當(dāng)實(shí)現(xiàn)了其中任意方法后,已經(jīng)配置的界面提供的處理程序會(huì)盡快執(zhí)行完成。如果等得太久,Apple Watch 會(huì)嘗試放棄顯示你的動(dòng)態(tài)界面而以顯示靜態(tài)界面來(lái)代替。
清單 16-1 展示了 didReceiveRemoteNotification:withCompletion:
方法實(shí)現(xiàn)的示例。這個(gè)方法是通過(guò)一個(gè)虛構(gòu)的日歷程序,發(fā)送一個(gè)新的會(huì)議邀請(qǐng)遠(yuǎn)程通知。該方法從遠(yuǎn)程通知的負(fù)載數(shù)據(jù)中提取數(shù)據(jù)并利用該數(shù)據(jù)來(lái)設(shè)置通知界面上的 label 值。為簡(jiǎn)單起見(jiàn),該示例假定服務(wù)器總是為每個(gè) key 都包含了一個(gè)合適的值。但你自己的代碼應(yīng)該做一些必要的錯(cuò)誤檢查以保證這些負(fù)載數(shù)據(jù)是有效的。在配置完 label 之后,則調(diào)用處理程序讓 WatchKit 知道自定義的界面以及準(zhǔn)備好可以顯示了。
清單 16-1 在一個(gè)遠(yuǎn)程通知里配置自定義界面
// 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)用完成處理代碼塊時(shí),如果你希望 WatchKit 顯示靜態(tài)界面,那就指定 WKUserNotificationInterfaceTypeDefault 常量。
注意
通知界面的文字僅僅只支持系統(tǒng)指定的文字字體,如果你需要顯示其他字體,最好是將文字嵌入圖片中,然后顯示那張圖片。
當(dāng)你已經(jīng)準(zhǔn)備好在模擬器上測(cè)試你的動(dòng)態(tài)界面時(shí),為你的通知界面創(chuàng)建一個(gè)自定義運(yùn)行計(jì)劃,如果你還沒(méi)有這么做的話。當(dāng)你配置界面時(shí),指定一個(gè)你希望發(fā)送到你的界面并包含測(cè)試數(shù)據(jù)的 JSON 文件。Xcode 會(huì)為指定的數(shù)據(jù)提供自定義的 JSON 文件。
更多關(guān)于設(shè)置運(yùn)行計(jì)劃并配置你的數(shù)據(jù)的信息,請(qǐng)見(jiàn) The Build, Run, Debug Process。