iOS10通知框架UserNotifications學(xué)習(xí)及兼容筆記

在iOS10上,蘋果將原來散落在UIKit中各處的用戶通知相關(guān)的代碼進行重構(gòu),剝離,打造了一個全新的通知框架-UserNotifications。筆者最近在開發(fā)公司通知相關(guān)的需求,跟著WWDC2016的視頻和官方文檔,學(xué)習(xí)了一下新框架。同時,在學(xué)習(xí)過程中,和老框架對應(yīng)Api進行對比,有了個人的感受和看法。

首先,對于通知框架,其框架功能包括以下四類

  • 申請權(quán)限/注冊配置
  • 發(fā)送本地通知
  • 展示和響應(yīng)本地/遠(yuǎn)程通知
  • App Extension

在UserNotifications框架中,最核心的類是UNUserNotificationCenter,這個類的是這三項功能的管理類,通過注入到currentNotificationCenter進行對消息的管理。而在之前,大部分操作的管理者UIApplication的單例。

1.申請權(quán)限/注冊配置

在新框架中,將申請權(quán)限和注冊配置拆分為兩個Api,使得職責(zé)更加分明。先看舊框架的實現(xiàn)。

UIUserNotificationType types = ...//通知可顯示的樣式

UIUserNotificationSettings *settting = [UIUserNotificationSettings settingsForTypes:types categories:nil]; //將樣式和Category一起生成配置

[[UIApplication sharedApplication] registerUserNotificationSettings:settting];//用這個配置注冊

Category的概念,是一個通知的種類。對同種類的通知,可以規(guī)定對應(yīng)的一組按鈕操作,同時,iOS10以上的Rich Notification(可以展示圖片,自定義視圖)的區(qū)分讀也是通過category來區(qū)分。

而在老的通知框架中,上面第二行代碼中可以看到,category和展示樣式一同綁定起來用于申請權(quán)限。實際上,Category的概念用于區(qū)分接收消息的種類,也就是其實這個概念屬于接收消息后進行自定義處理的使用者,也就是App內(nèi)部開發(fā)。而對于申請權(quán)限,其實最終是向App外部的交互,也就是App用戶關(guān)心的。而新框架進行拆分后,就更加自由和職責(zé)區(qū)分了。

UserNotifications中請求權(quán)限的用法

[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:... completionHandler:^(BOOL granted, NSError * _Nullable error) {
        if (granted) {
            //...如果被授權(quán)了
        }
}];

同時,增加一個callback將授權(quán)后的狀態(tài)返回,就像分散型網(wǎng)絡(luò)請求一般。

對于遠(yuǎn)程通知,還需要一個獲取用戶token的操作,并使用這個token進行APNs推送。但是奇怪的是,對于申請token這個操作,新框架中卻沒有對應(yīng)的Api,沿用舊的Api。

[[UIApplication sharedApplication] registerForRemoteNotifications];

//AppDelegate
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
  ....
}

獲取操作被放在AppDelegate的回調(diào)中,如果按照新框架的設(shè)計,應(yīng)該會被設(shè)計成一個帶callBack的Api。所以筆者猜測沿用舊設(shè)計的原因是,在以后,獲取token的操作不一定由發(fā)起請求后產(chǎn)生,也許可能會有多種情況下產(chǎn)生獲取操作,也可能token會過期可以進行更新,重置操作。所以delegate的集中式管理更加擁有拓展性,當(dāng)然也有可能這個Api忘記遷移哈哈。

配置Category并注冊

對于Category的作用,上文已經(jīng)有介紹,而Category中目前主要的功能,是對應(yīng)一組按鈕操作。

Category的使用分四步

  1. 創(chuàng)建Category,設(shè)置identifer,配置一組按鈕(可無)
  2. 將Category注冊到UIApplication(舊)/UNNotificationCenter(新)中
  3. 發(fā)送本地或遠(yuǎn)程消息時需要按鈕或自定義視圖時帶上對應(yīng)的categoryIdentifer
  4. 收到消息進行響應(yīng)時通過消息中攜帶的categoryIdentifer進行分類,可以指定不同的操作

舊框架中按鈕動作對象UIUserNotificationAction和新的UNNotificationAction差異性不大,主要關(guān)心的是

  • 按鈕上顯示的文字
  • 按鈕的identifer,用于區(qū)分按鈕
  • 是否需要跳轉(zhuǎn)到前臺,喚起主App

而差異性在于

  • 舊框架分為可變對象和不可變對象,新框架只有不可變對象加上實例化方法
  • 新框架中,對于可輸入操作的按鈕,拆分成一個子類,可以使輸入操作自定義性和拓展性更強

新建一個按鈕也很簡單

UNNotificationAction *callDriverAction = [UNNotificationAction actionWithIdentifier:@"xxx" title:@"呼叫司機" options:UNNotificationActionOptionForeground];

options中可以選擇是否需要解鎖后才能操作、按鈕顏色是否為紅色(代表操作有破壞性)、是否打開App。

然后將整組按鈕加入UNNotificationCategory(新)或UIUserNotificationCategory(舊)中。

這兩個對象差異性在于

  • 舊框架分為可變對象和不可變對象,新框架只有不可變對象加上實例化方法
  • 舊框架按鈕有兩種使用環(huán)境,所以需要設(shè)置UIUserNotificationActionContext
  • 新框架支持配置按鈕的響應(yīng)是否發(fā)送到UNNotificationCenter的Delegate或CarPlay中
  • 新框架支持Intent框架(SiriKit使用到)
UIUserNotificationActionContext
  • UIUserNotificationActionContextDefault-對應(yīng)iOS10以下的的“提醒”樣式,是一個彈框
  • UIUserNotificationActionContextMinimal-對應(yīng)iOS10以下的“橫幅”樣式,最多支持兩個按鈕橫向并排,多于兩個按鈕會取前兩個

為什么這個在新的框架中被去掉了呢?

  • iOS10上,提醒樣式不再是一個彈框,而是和“橫幅”統(tǒng)一,只是不會主動往上收起
  • iOS10上,橫幅的按鈕不再是橫向并排,而是豎著排放,也不會限制個數(shù)

之后調(diào)用setNotificationCategories方法注冊即可

[[UNUserNotificationCenter currentNotificationCenter]setNotificationCategories:[NSSet setWithObject:self.category]];

在運行app時,原來的set里的categories并不會清空,所以需要將整個Set傳進去作為參數(shù),這樣會把原來的set完整替換成新的set。每次調(diào)用時,整個set替換原來的set。

同時,對于通知設(shè)置和Category設(shè)置,拆分了兩個Api去獲取當(dāng)前的設(shè)置。

- (void)getNotificationCategoriesWithCompletionHandler:(void(^)(NSSet<UNNotificationCategory *> *categories))completionHandler;

- (void)getNotificationSettingsWithCompletionHandler:(void(^)(UNNotificationSettings *settings))completionHandler;

而對于按鈕的操作響應(yīng),將在第三部分響應(yīng)通知中介紹。

2.發(fā)送本地通知

在iOS10以前,本地通知的類用的是UILocalNotification。而在UserNotifications框架中,將本地通知和遠(yuǎn)程通知統(tǒng)一起來,然后將通知拆分成request=content(內(nèi)容)+trigger(觸發(fā)器)的模式,十分像網(wǎng)絡(luò)請求的思路。這樣設(shè)計的好處是對于遠(yuǎn)程通知和本地通知對于響應(yīng)的處理得到統(tǒng)一,同時也不會像以前一樣,將本地特有的功能隨意堆砌在本地通知類中,而是通過差異化配置類去進行注入。

UNNotificationContent

新的通知內(nèi)容類,有可變子類。同時有以下新功能

  • 支持一個UNNotificationAttachment(附件)數(shù)組,附件用一個identifer+文件路徑構(gòu)成,可攜帶視頻/圖片等,而這些內(nèi)容也為iOS10的RichNotification自定義視圖提供了素材
  • 支持Title+Subtitle,Apns對應(yīng)字段也同步支持
  • 通知聲音有了特定類UNNotificationSound進行管理,以后拓展性,自由度更好了
  • threadIdentifer(主要用于Extesion Content中,在最后一節(jié)App Extension中會介紹)

UNNotificationTrigger

而之前散落成一個個不同類型的觸發(fā)相關(guān)屬性,也被匯總成了UNNotification的子類。有以下幾個類

  • UNPushNotificationTrigger-這個類代表這條消息是由APNs推送過來的,也就是這個trigger是否是這個類是區(qū)分本地通知和遠(yuǎn)程通知的標(biāo)志,對于做多系統(tǒng)b版本兼容時,很有幫助
  • UNTimeIntervalNotificationTrigger-根據(jù)相隔時間觸發(fā),就和計時器一樣,注意timeInterval要大于0,且希望repeats的話,需要timeInterval大于60
  • UNCalendarNotificationTrigger-根據(jù)日歷時間觸發(fā)
  • UNLocationNotificationTrigger-根據(jù)定位在某個位置觸發(fā)

UNNotificationRequest

request除了包含content和trigger外,還有一個非常主要的屬性,那就是identifer

有了Identifer,可以實現(xiàn)通知的更新和移除。

把通知分成待展示(已投遞,但未觸發(fā)或未展示)和已經(jīng)展示過進入用戶通知中心的兩種。遠(yuǎn)程通知在后臺收到會立即展示。也就是一共有四種情況

  • 更新未展示的通知(本地&遠(yuǎn)程前臺)
  • 更新已展示的通知(本地&遠(yuǎn)程)
  • 取消未展示的通知(本地)
  • 取消已展示的通知(本地)

而標(biāo)識同一通知的標(biāo)志,就是通知的identifer。

而發(fā)送/更新通知用的是同一個Api,就是將request添加到UNNotificationCenter里,同時也有一個callback回調(diào)狀態(tài)

[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:[UNNotificationRequest requestWithIdentifier:sameIdentifer content:content trigger:trigger] withCompletionHandler:^(NSError * _Nullable error) {
        ...
}];

而取消或移除未展示和已展示的通知,分開了兩組Api方便根據(jù)情況選擇,同時也有g(shù)et方法獲取當(dāng)前已展示/未展示的隊列里的所有通知,而且和添加/更新不同,移除只需要一個identifer,且可以同時傳入一組identifer,一次性移除多個

- (void)getPendingNotificationRequestsWithCompletionHandler:(void(^)(NSArray<UNNotificationRequest *> *requests))completionHandler;//獲取未展示通知
- (void)removePendingNotificationRequestsWithIdentifiers:(NSArray<NSString *> *)identifiers;//取消未展示通知
- (void)removeAllPendingNotificationRequests;//取消所有未展示通知隊列里的通知

- (void)getDeliveredNotificationsWithCompletionHandler:(void(^)(NSArray<UNNotification *> *notifications))completionHandler;//獲取已展示通知
- (void)removeDeliveredNotificationsWithIdentifiers:(NSArray<NSString *> *)identifiers;//從通知中心中移除已展示通知
- (void)removeAllDeliveredNotifications;//移除所有通知中心里的通知

3.展示和響應(yīng)

在舊框架里面,這部分Api是最為混亂的部分。一共有1..2.....7個delegate......而且之中有的還有取代關(guān)系,也就是有其中一個另一個不執(zhí)行......

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo;//前臺收到遠(yuǎn)程通知

- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification;//前臺收到本地通知

- (void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void(^)())completionHandler;//iOS9之前本地通知點擊按鈕后

- (void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void(^)())completionHandler;//iOS9遠(yuǎn)程通知點擊按鈕后

- (void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler;//iOS9之前遠(yuǎn)程通知點擊按鈕后

- (void)application:(UIApplication *)application handleActionWithIdentifier:(nullable NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void(^)())completionHandler;//iOS9本地通知點擊按鈕

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler;//iOS7以上收到遠(yuǎn)程通知調(diào)用(未被廢棄)

這么亂,讓我重新分類。

  • 收到通知
  • 點擊通知本身
  • 點擊通知的自定義按鈕

收到通知

在觸發(fā)本地通知時,App并不會被喚醒,所以本地只有前臺時才有回調(diào)。

  • 本地+前臺-didReceiveLocalNotification
  • 遠(yuǎn)程+前臺-didReceiveRemoteNotificationdidReceiveRemoteNotification:fetchCompletionHandler若有后面則只執(zhí)行后面那個
  • 遠(yuǎn)程+后臺-喚醒并執(zhí)行didReceiveRemoteNotification:fetchCompletionHandler

點擊通知本身

  • 本地+App存活-didReceiveLocalNotification

  • 本地+App未存活--(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions,其中l(wèi)anchOptions中UIApplicationLaunchOptionsLocalNotificationKey為UILocalNotification對象

  • 遠(yuǎn)程+App存活-didReceiveRemoteNotificationdidReceiveRemoteNotification:fetchCompletionHandler若有后面則只執(zhí)行后面那個

  • 遠(yuǎn)程+App未存活-didReceiveRemoteNotification:fetchCompletionHandler有則執(zhí)行,無則-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions,其中l(wèi)anchOptions中UIApplicationLaunchOptionsRemoteNotificationKey

    為通知的userInfo

點擊自定義按鈕

  • iOS9handleActionWithIdentifier,有responseInfo后綴的優(yōu)先調(diào),無則調(diào)用無后綴的
  • iOS8調(diào)用無后綴的

整理完感覺就是,亂,還涉及到lauchOptions等無關(guān)api,api之間還會覆蓋,還有一些不是收到和響應(yīng)都會調(diào)。

而新框架單純拆分為收到消息+點擊事件響應(yīng),且不再在Api層面區(qū)分遠(yuǎn)程與本地通知,使對待通知的路徑變得統(tǒng)一。

通知的收到

在前臺的時候,收到不管是本地還是遠(yuǎn)程通知都會被新的delegate接管。以前的通知框架,在前臺收到通知時,默認(rèn)是不展示(沒有橫幅等)的。而新delegate可以在前臺收到通知后,展示前,做一些處理,包括以什么形式展示,已經(jīng)做一些自定義操作。而這個Delegate只有App在前臺才會執(zhí)行。因為在后臺App收到通知時,通知不會喚醒App(不是喚起),而App可能是被殺死的,所以這個Delegate沒有被賦值,所以統(tǒng)一只在App在前臺時才執(zhí)行。而將新的Api對應(yīng)舊的Api轉(zhuǎn)換起來的話,就是下面的代碼。

- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{
    BOOL isRemote = NO;
    if ([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        isRemote = YES;
    }
    UILocalNotification *localNotification;//從新的轉(zhuǎn)換為舊的本地通知
    
     if (!isRemote && [[UIApplication sharedApplication].delegate respondsToSelector:@selector(application:didReceiveLocalNotification:)]) {
        [[UIApplication sharedApplication].delegate application:[UIApplication sharedApplication] didReceiveLocalNotification:localNotification];
    }
    else if (isRemote) {
        if ([[UIApplication sharedApplication].delegate respondsToSelector:@selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]) {
            [[UIApplication sharedApplication].delegate application:[UIApplication sharedApplication] didReceiveRemoteNotification:notification.request.content.userInfo fetchCompletionHandler:^(UIBackgroundFetchResult result) {}];
        }
        else if ([[UIApplication sharedApplication].delegate respondsToSelector:@selector(application:didReceiveRemoteNotification:)]){
            [[UIApplication sharedApplication].delegate application:[UIApplication sharedApplication] didReceiveRemoteNotification:notification.request.content.userInfo];
        }
    }

    
    completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionAlert);//決定前臺展示形式
}

而CompletionHandler里傳入希望通知支持的類型(橫幅、紅點、聲音),在自定義操作完成后執(zhí)行這個block就可。當(dāng)然可以在這里為不同消息定制不同的支持類型。

點擊通知或按鈕的操作響應(yīng)

新框架中,點擊操作和收到通知的Api的響應(yīng)終于被區(qū)分開了。而且點擊操作也包含點擊通知本身,以及有了點清除關(guān)閉通知的事件響應(yīng)。拆分點擊和收到的Api是很好的,因為點擊通知和按鈕時,實際上會將app喚起到前臺,一般這時候需要進行頁面跳轉(zhuǎn),界面狀態(tài)恢復(fù)等等,這些在收到通知時是沒有必要的。

新的Api和就的轉(zhuǎn)換的話就是下面這樣

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler{
    BOOL isRemote = NO;
    if ([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
        isRemote = YES;
    }
    
    UILocalNotification *localNotification;//從新的轉(zhuǎn)換為舊的本地通知

    
    if ([response.actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier] || [response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier]) {
        if (!isRemote && [[UIApplication sharedApplication].delegate respondsToSelector:@selector(application:didReceiveLocalNotification:)]) {
            [[UIApplication sharedApplication].delegate application:[UIApplication sharedApplication] didReceiveLocalNotification:localNotification];
        }
        else if (isRemote) {
            if ([[UIApplication sharedApplication].delegate respondsToSelector:@selector(application:didReceiveRemoteNotification:fetchCompletionHandler:)]) {
                [[UIApplication sharedApplication].delegate application:[UIApplication sharedApplication] didReceiveRemoteNotification:response.notification.request.content.userInfo fetchCompletionHandler:^(UIBackgroundFetchResult result) {}];
            }
            else if ([[UIApplication sharedApplication].delegate respondsToSelector:@selector(application:didReceiveRemoteNotification:)]){
                [[UIApplication sharedApplication].delegate application:[UIApplication sharedApplication] didReceiveRemoteNotification:response.notification.request.content.userInfo];
            }
        }
    }
    else{
        if (!isRemote) {
            if ([[UIApplication sharedApplication].delegate respondsToSelector:@selector(application:handleActionWithIdentifier:forLocalNotification:withResponseInfo:completionHandler:)]) {
                [[UIApplication sharedApplication].delegate application:[UIApplication sharedApplication] handleActionWithIdentifier:response.actionIdentifier forLocalNotification:localNotification withResponseInfo:@{} completionHandler:^{}];
            }
            else if ([[UIApplication sharedApplication].delegate respondsToSelector:@selector(application:handleActionWithIdentifier:forLocalNotification:completionHandler:)]){
                [[UIApplication sharedApplication].delegate application:[UIApplication sharedApplication] handleActionWithIdentifier:response.actionIdentifier forLocalNotification:localNotification completionHandler:^{}];
            }
        }
        else{
            if ([[UIApplication sharedApplication].delegate respondsToSelector:@selector(application:handleActionWithIdentifier:forRemoteNotification:withResponseInfo:completionHandler:)]) {
                [[UIApplication sharedApplication].delegate application:[UIApplication sharedApplication] handleActionWithIdentifier:response.actionIdentifier forRemoteNotification:response.notification.request.content.userInfo withResponseInfo:@{} completionHandler:^{}];
            }
            else if ([[UIApplication sharedApplication].delegate respondsToSelector:@selector(application:handleActionWithIdentifier:forRemoteNotification:completionHandler:)]){
                [[UIApplication sharedApplication].delegate application:[UIApplication sharedApplication] handleActionWithIdentifier:response.actionIdentifier forRemoteNotification:response.notification.request.content.userInfo completionHandler:^{}];
            }
            
        }
    }

    }
    completionHandler();
}

當(dāng)實現(xiàn)了新框架這兩個Delegate后,舊框架的6個Delegate將不會被執(zhí)行,所以除非App只支持iOS10,盡量按上面的代碼進行兼容。

**注意-(void)application:(UIApplication )application didReceiveRemoteNotification:(NSDictionary )userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler;這個方法并未隨著舊框架被廢棄,還是正常使用。

4.App Extension

關(guān)于新通知框架的Extension,有以下兩個

屏幕快照 2017-07-26 下午3.16.09

NotificationService Extension

這個Extension允許我們在遠(yuǎn)程通知收到前做一些修改。因為之前App內(nèi)UNNotificaionCenter的Delegte并不能在App不存活情況下執(zhí)行,所以有了這個Extension來提供這樣的功能。新建NotificationService的target后,發(fā)現(xiàn)系統(tǒng)會自動生成了UNNotificationServiceExtension的子類。其中重寫方法的模板,系統(tǒng)也生成好了。

@interface NotificationService ()
@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
@end

@implementation NotificationService

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    self.contentHandler(self.bestAttemptContent);
}

- (void)serviceExtensionTimeWillExpire {

    self.contentHandler(self.bestAttemptContent);
}

@end

第一個方法在收到遠(yuǎn)程通知時,可以攔截通知并修改通知內(nèi)容。先用一個block保存當(dāng)時的上下文,再修改完后再調(diào)用block再修改完新的通知內(nèi)容回調(diào)回去。

第二個方法是在修改通知時機將要結(jié)束時調(diào)用,這時候會強制執(zhí)行block了。

在推送 payload 中增加一個 mutable-content 值為 1 來啟用這個Extension,暫時還不支持本地通知。

NotificationContent Extension

這個就是實現(xiàn)Rich Notification的拓展。新建這個Target后,可以發(fā)現(xiàn)自動生成了一個ViewController并遵循了UserNotificationsUI框架的UNNotificationContentExtension協(xié)議。又是一個新框架,看來之后在通知視圖上的功能將會變得更強大。

- (void)didReceiveNotification:(UNNotification *)notification {
    //收到通知后的操作
}

- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption option))completion{
   //收到通知響應(yīng),如按鈕,可以這個方法里攔截,并自定義操作,再決定是否將按鈕放行至主app
}

這個協(xié)議里面只有上面兩個方法。第一個方法用于在展開Rich視圖時觸發(fā),進行根據(jù)數(shù)據(jù)去改變視圖。就如平時收到網(wǎng)絡(luò)請求后根據(jù)網(wǎng)絡(luò)請求返回的響應(yīng)改變視圖元素。當(dāng)打開RIch視圖后,這個方法還有兩個時機會觸發(fā)。

  1. Request的Identifer一致,相當(dāng)于更新整個通知,同時視圖也重新加載
  2. 還記得之前說的Content的threadIdentifer么,這個相同時,這個方法也會重新調(diào)用,也就是這個是用來標(biāo)識同一個流程的的通知,當(dāng)然,通知還是兩條,只是視圖從通知A狀態(tài)到通知B狀態(tài)了

而第二個方法,實在通知響應(yīng)時,比如按鈕響應(yīng)時,先在這個Extension里攔截,可以進行響應(yīng)修改,視圖改變等操作,然后通過CompletionHandler的UNNotificationContentExtensionResponseOption來選擇放行這些響應(yīng)。其中UNNotificationContentExtensionResponseOptionDismissAndForwardAction才可以將響應(yīng)放行到主app里UNNotificationCenter的delegate方法中。

同時這個Extension也有自己的Storyboard和Info.plist。

DefaultContenHidden代表是否在Rich狀態(tài)下還顯示原來的body文字

Category代表此類categoryIdentifer會觸發(fā)這個ViewController,可以是一個Array,多個同時支持這個ViewController。

SizeRatio代表高/寬比,其中寬固定為屏幕寬度,也就是用這個來調(diào)整視圖的高度。

iOS11New

都是一些小改動,增加一些細(xì)分化Api等,可以在蘋果官方文檔找到。

https://developer.apple.com/documentation/usernotifications?changes=latest_major&language=objc

總結(jié)

iOS10新通知框架和舊框架差異性

UILocalNotification UNNotificationRequest Apns消息體
identifer HTTP/2的header中的 apns-collapse-id
content.attachments(UNNotificationAttachmen) 可在UNNotificationService中修改
applicationIconBadgeNumber(Integer) content.badge badge
alertBody content.body alert的body
category content.categoryIdentifer category
alertLaunchImage lanuchImageName(打開時啟動圖) alert的lanch-image
soundName(新無法轉(zhuǎn)換為舊) UNNotificationSound-soundNamed: sound
subtitle alert的subtitle
threadIdentifer(特殊標(biāo)識符,自定義用) thread-id
alertTitle(iOS8.2以上) title alert的title
userInfo userInfo 所有
fireDate UNNotificatonTimeInterval-trigger.timeInterval
repeatInterval>0 或 repeatCalendar不為空或regionTriggersOnce為YES trigger.repeat
alertAction(alert模式下action名字)
region(觸發(fā)位置) UNLocationNotificationTrigger-trigger.region
timeZone
repeatInterval(重復(fù)時間點) UNNotificationTimeInterval/Calendar-nextTriggerDate轉(zhuǎn)換
reatCalendar(重復(fù)的日歷) trigger的Class為UNNotificationPushTrigger
regionTriggersOnce UNLocationNotificationTrigger-trigger.repeats
hasAction(alert下是否有動作)
UNNotificationAction UIUserNotificationAction
identifer Identifer
title title
UNNotificationActionOptions-options activationMode+authenticationRequired+destructive共同決定
UNTextInputNotificationAction/UNNotification(按鈕種類) UIUserNotificationActionBehavior-behavior(iOS9以上)
parameters
UNTextInputNotificationAction-textInputButtonTitle
UNTextInputNotificationAction-textInputPlaceholder
UNNotificationCategory UIUserNotificationCategory
identifer Identifer
actions(UNNotificationAction) actionsForContext(UIUserNotificationAction)
intentIdentifers(支持Siri或蘋果叫車框架的標(biāo)識符)
options(點擊后是否消失等等)

iOS10的通知框架,和之前比簡直煥然一新,整潔,優(yōu)雅。還有一點需要注意的是,因為原來屬于UIKit中,那么最好在主線程中操作。但由于老框架暫時還不會被廢棄,所以在接入時,要注意新老框架的兼容和覆蓋,避免出現(xiàn)不必要的Bug。

PS:此文又名《iOS10通知框架最全總結(jié)》

PPS:此文又名《iOS10通知框架,你看我就夠了》

PPPS:此文又名《iOS10通知框架,你真的懂么?》

所有源碼和Demo
如果您覺得有幫助,不妨給個star鼓勵一下,歡迎關(guān)注&交流

有任何問題歡迎評論私信或者提issue
QQ:757765420
Email:nemocdz@gmail.com
Github:Nemocdz
微博:@Nemocdz

謝謝觀看

參考鏈接
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,462評論 2 378

推薦閱讀更多精彩內(nèi)容