[iOS 10 day by day] Day 6:自定義的通知介面

發表於2016-10-26

續上篇,在簡單鬧鐘的例子上,在通知介面上顯示圖片動畫,並用通知關聯的按鈕更新通知介面。介紹 iOS 10 通知 API 的擴充套件:自定義通知顯示介面。

《iOS 10 day by day》是 shinobicontrols 公司編寫的系列部落格,介紹開發者需要了解的 iOS 10 新特性,每週更新。本系列翻譯(文集地址)已取得官方授權。目錄點此。倉薯翻譯,歡迎指正:)

Shinobicontrols 為 iOS 和 Android 開發者提供高效能、響應式的 UI 控制元件 SDK,尤其是圖表方面的控制元件。 官網 : shinobicontrols.com twitter : @shinobicontrols

我們在 Day 5 中介紹了新的 UserNotifications 框架。新框架可以統一處理本地通知和遠端推送,同時增加了一些新 API 來控制等待中和已發出的通知。

以上這些都很棒,不過蘋果還在通知方面更進一步,讓開發者能新增一個自定義的通知介面,使用者收到通知之後可以選擇檢視這個自定義介面。要實現這個功能,需要新增一個單獨的 UserNotificationsUI 框架。這個框架的 API 特別簡單,只含有一個公共的 protocol:UNNotificationContentExtension

工程

我們的樣例工程是在上一篇文章的鬧鐘 app 基礎上,增加了一個炫酷的自定義通知介面。通過這個介面,使用者可以不用切換到鬧鐘 app 就能直接取消通知。先來看下效果:

11227290-0c09d911ff05670f
自定義通知介面效果

跟所有 Day by Day 系列文章一樣,工程原始碼放在了 Github 上。

建立 Extension

iOS 10 的許多旗艦功能都是建立在蘋果的 Extension 架構上的。前面的系列文章 Xcode 外掛iMessage 外掛 都是如此。而自定義通知介面也是用同樣的方法實現的。

首先,我們要給鬧鐘 app 的工程加一個新的 target。在下面這個選擇 target 模板的介面,選擇 Notification Content。然後隨便起個名字,我用的是 NagMeContentExtension

12227290-457495502483a4e0
選擇 target 模板

你可能會注意到,除了預設的Info.plist之外,這個 extension 還包含另外兩個檔案:

  • MainInterface.storyboard : 我們把自定義通知介面的 UI 畫在這裡
  • NotificationViewController.swift : 一個 UIViewController 的子類,這就是自定義介面的 ViewController,我們通過這個類來管理自定義的介面。

把 Extension 與通知 category 關聯起來

現在工程設定好了,我們需要讓系統知道,是哪個通知要展示這個介面。不知道你記不記得,上一篇文章講過,一個 category 就是一個很簡單的物件(參考 UNNotificationCategory),裡面定義了你的 app 支援哪些型別的通知,以及每種通知關聯了什麼操作——就是使用者把通知展開的時候,通知下面出現的那些操作按鈕。

具體實現這一步,需要開啟 extension 的 Info.plist,展開 NSExtensionAttributes Dictionary,把下面 UNNotificationExtensionCategory 這個鍵對應的值改為通知 category 的名字(“reminder”)。注意,這個值既可以填一個 string ,也可以填一個 string 陣列,如果想讓多個通知 category 共用一個 extension 介面就可以填 string 陣列。

13227290-9b24c7f12530f35a
Info.plist

現在把工程 Build、Run 一下,我們可以看到一個比預設的通知彈框更有意思一點的介面。

14227290-a353d8b38a265c5d
extension 的預設介面

管用了!現在用的是 extension 預設的 MainInterface.storyboard 介面,然後是 NotificationViewController 裡的模板程式碼在更新介面上的 label。不過這個介面還是有幾點需要改進的地方。首先,通知的內容(”Walk Dog!!”)在 extension 的介面上和 DefaultContent 區域重複出現了兩次。我們先把這個重複的去掉吧!

去掉 DefaultContent

很簡單,只需在 Info.plist 檔案裡的 NSExtensionAttributes 下面增加一個 key ,UNNotificationExtensionDefaultContentHidden,然後值設為 YES,就不會顯示 DefaultContent 了。

15227290-92ea97efc85da478
去掉 default content 之後

好,下面我們來寫自定義的介面吧。

自定義的通知介面

切換到 MainInterface.storyboard,加上 UI 控制元件。加一個 label 描述提醒的事項,加一個小喇叭的圖片。加完之後,只需拖幾個 IBOutlets 出來,就大功告成啦!

收到通知的時候,我們要更新 label 上的文字,同時搖晃小喇叭的圖片——用這種粗暴的方式吸引使用者的注意力。要實現這些功能,需要在 NotificationViewController 裡進行一些修改。我們的 viewController 實現了 UNNotificationContentExtension 這個 protocol,下面用到的就是這個 protocol 中定義的方法:

這個方法就是收到通知之後,根據通知內容來配置通知介面的指定方法。

16227290-6c1c7886eec1875c
初步的通知介面

看起來還不錯,但是中間有一大段空白,看上去不大美觀。

幸運的是,要解決這個問題只需加 Info.plist 裡再加一個 key UNNotificationExtensionInitialContentSizeRatio,它定義了自定義通知介面的高寬比。這個值可能需要多試幾次來調整,對於我們目前的情況取 0.5 就比較合適了(當寬度是 300 的時候,高度是 150)。

17227290-c6a3ef31d3366d9c

調整高寬比之後的介面

NotificationViewController 就是一個單純的 UIViewController 的子類,用起來跟你平常在主 app 裡用普通的 viewController 是一樣的。唯一的不同點在於它的 userInteraction 是 disabled 的,意思是完全無法接收到使用者的點選、觸控事件。所以有部分控制元件是用不了的,比如 UIScrollView、UIButton 等。

接受使用者操作

自定義的介面我們畫出來了,但是還有一點要改進:點選 “Cancel” 按鈕,只會讓使用者切回到鬧鐘 app,這一步有點多餘。

上一篇文章我們講了怎麼給通知加上操作按鈕:通知出現時可以進行的每一項操作都是一個 UNNotificationAction,關聯在通知 category 上。更詳細的介紹可以參考官方文件

UNNotificationContentExtension 這個 protocol 提供了另一個處理點選事件的方法:didReceive(_:completionHandler:)。我們就用這個方法,把小喇叭的 icon 改成紅線劃掉的小喇叭,然後把通知從 UNNotificationCenter 中移除。

相關的通知都移除了,UI 也更新了,接下來我們需要告訴系統該怎麼處置這個通知介面。因為我們想讓使用者看到被劃掉的小喇叭,得到通知被取消的視覺反饋,所以要把通知留在螢幕上,因此回撥裡傳入 UNNotificationContentExtensionResponseOption 的一個取值 .doNotDismiss

18227290-8ceac0cb657dfe5d
取消通知

既然要用這個方法處理點選,就得處理好每一個按鈕事件。在這個例子裡,我們只有一個“Cancel”按鈕。然而,如果還有別的按鈕,它們的點選事件也需要處理好:要麼也在 extension 工程的這個方法裡處理,要麼回撥傳 UNNotificationContentExtensionResponseOption.dismissAndForwardAction,傳給主 app 去處理。

擴充套件閱讀

UserNotificationsUI 這個框架並沒有什麼驚天動地的突破,但它能讓使用者與 app 的互動更便捷。使用者可以直接對通知進行操作,不用再切換到發出通知的 app 了;甚至通知介面的 UI 也能動態改變,來更好地反饋使用者操作的結果。

關於通知的其他“高階”特性,我推薦看看 WWDC 2016 的演講視訊。這場視訊中,演講者給出了幾個蘋果官方 app 自定義通知介面的例子,比如接收日程邀請。