iOS 10的UserNotifications 框架解析及使用

写在最前

在iOS 10中,之前有关Remote Notification和Local Notification的的杂乱的API被进行了重构,并在iOS 8 以及iOS 9的基础之上进行了功能新的扩展,推出了全新并且独立的 UserNotifications.framework 来集中管理相关通知的各种API。

相对于开发者来说使用起来更加简单易用,相对于运营环节来说,因为新的框架有了Attachment以及自定义UI等新的功能,所以有更加灵活的方法来处理相关的通知。

接口分析

UserNotifications.h

以上为新的UserNotifications.framework 为我们带来的各项接口。

1.<UserNotifications/NSString+UserNotifications.h>

类中只有一个方法

// Use -[NSString localizedUserNotificationStringForKey:arguments:] to provide a string that will be localized at the time that the notification is presented.
+ (NSString *)localizedUserNotificationStringForKey:(NSString *)key arguments:(nullable NSArray *)arguments __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0) __TVOS_PROHIBITED;

主要作用是用来提供将在该通知被呈现在时间进行本地化字符串。

2.<UserNotifications/UNError.h>

只有一个属性UNErrorDomain以及一个枚举UNErrorCode,顾名思义是作为错误属性的值。

3.<UserNotifications/UNNotification.h>

拥有两个属性

// The date displayed on the notification.
@property (nonatomic, readonly, copy) NSDate *date;
// The notification request that caused the notification to be delivered.
@property (nonatomic, readonly, copy) UNNotificationRequest *request;

一般作为返回的消息实例。

4.<UserNotifications/UNNotificationAction.h>

从名字就可以看出,类的作用就是定义了收到通知后的一些Action操作,有identifier,title,options三个属性,其中options是一个枚举类型

typedef NS_OPTIONS(NSUInteger, UNNotificationActionOptions) {

    // Whether this action should require unlocking before being performed.
    //button为红色,点需要解锁屏幕显示
    UNNotificationActionOptionAuthenticationRequired = (1 << 0),
    // Whether this action should be indicated as destructive
    //button为黑色,点击不会进入App
    UNNotificationActionOptionDestructive = (1 << 1),
    // Whether this action should cause the application to launch in the foreground.
    //button为黑色,点击进入App
    UNNotificationActionOptionForeground = (1 << 2),
} __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0) __TVOS_PROHIBITED;

拥有有一个子类UNTextInputNotificationAction,所谓类似回复微信和iMessage时候使用。

5.<UserNotifications/UNNotificationAttachment.h>

通知的附件属性类,里面三个主要属性

// The identifier of this attachment
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSString *identifier;

// The URL to the attachment's data. If you have obtained this attachment from UNUserNotificationCenter then the URL will be security-scoped.
//附件相关的地址,可为本地地址
@property (nonatomic, readonly, copy) NSURL *URL;

// The UTI of the attachment.
@property (nonatomic, readonly, copy) NSString *type;

另有4个常量

// Key to manually provide a type hint for the attachment. If not set the type hint will be guessed from the attachment's file extension. Value must be an NSString.
//手动提供附件类型提示的键。如果没有设置类型提示,将会从附件文件扩展名中猜出类型。值必须是一个NSString格式。
extern NSString * const UNNotificationAttachmentOptionsTypeHintKey __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);

// Key to specify if the thumbnail for this attachment is hidden. Defaults to NO. Value must be a boolean NSNumber.
// 注明此附件缩略图是隐藏的键。默认值为NO。值必须是一个NSNumber类型的布尔值。
extern NSString * const UNNotificationAttachmentOptionsThumbnailHiddenKey __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);

// Key to specify a normalized clipping rectangle to use for the attachment thumbnail. Value must be a CGRect encoded using CGRectCreateDictionaryRepresentation.
//指定一个标准化的剪辑矩形用于附件缩略图的键。值必须是使用DictionaryRepresentation编码的一个CGRect值。
extern NSString * const UNNotificationAttachmentOptionsThumbnailClippingRectKey __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);

// Key to specify the animated image frame number or the movie time to use as the thumbnail.
// An animated image frame number must be an NSNumber. A movie time must either be an NSNumber with the time in seconds or a CMTime encoded using CMTimeCopyAsDictionary.
//指定动画形象帧数或用缩略图的电影时间的键。
//动画图像帧数必须是一个NSNumber对象。电影的时间必须是一个NSNumber对象的以秒为单位的时间或使用CMTimeCopyAsDictionary编码的CMTime值。
extern NSString * const UNNotificationAttachmentOptionsThumbnailTimeKey __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);
6.<UserNotifications/UNNotificationCategory.h>

用于储存Action的类

// The unique identifier for this category. The UNNotificationCategory's actions will be displayed on notifications when the UNNotificationCategory's identifier matches the UNNotificationRequest's categoryIdentifier.
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSString *identifier;

// The UNNotificationActions in the order they will be displayed.
//储存我们设置的Actions的数组
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSArray<UNNotificationAction *> *actions;

// The intents supported support for notifications of this category. See <Intents/INIntentIdentifiers.h> for possible values.
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSArray<NSString *> *intentIdentifiers;

@property (NS_NONATOMIC_IOSONLY, readonly) UNNotificationCategoryOptions options;

options是一个权限的枚举值,两者可以一起使用

typedef NS_OPTIONS(NSUInteger, UNNotificationCategoryOptions) {
    UNNotificationCategoryOptionNone = (0),

    // Whether dismiss action should be sent to the UNUserNotificationCenter delegate
    UNNotificationCategoryOptionCustomDismissAction = (1 << 0),

    // Whether notifications of this category should be allowed in CarPlay
    UNNotificationCategoryOptionAllowInCarPlay = (2 << 0),

} __IOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0) __TVOS_PROHIBITED;
7.<UserNotifications/UNNotificationContent.h>

可以简单看做是储存所有通知信息的Model类,里面有attachments、sound、title以及userInfo等信息。

拥有一个子类UNMutableNotificationContent,和父类的区别从名字就可以看出是可以修改的,个属性中也比父类少了一个readonly属性。

8.<UserNotifications/UNNotificationRequest.h>

有三个属性,分别是标示符、消息Model以及NotificationTrigger。

// The unique identifier for this notification request. It can be used to replace or remove a pending notification request or a delivered notification.
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSString *identifier;

// The content that will be shown on the notification.
@property (NS_NONATOMIC_IOSONLY, readonly, copy) UNNotificationContent *content;

// The trigger that will or did cause the notification to be delivered. No trigger means deliver now.
@property (NS_NONATOMIC_IOSONLY, readonly, copy, nullable) UNNotificationTrigger *trigger;
9.<UserNotifications/UNNotificationResponse.h>

通过名字就可以看出来,与request对应作为相应通知的Action使用。
用户可以拿到整个通知的内容,以及操作所对应的标示符。

// The notification to which the user responded.
@property (NS_NONATOMIC_IOSONLY, readonly, copy) UNNotification *notification;

// The action identifier that the user chose:
// * UNNotificationDismissActionIdentifier if the user dismissed the notification
// * UNNotificationDefaultActionIdentifier if the user opened the application from the notification
// * the identifier for a registered UNNotificationAction for other actions
@property (NS_NONATOMIC_IOSONLY, readonly, copy) NSString *actionIdentifier;

子类UNTextInputNotificationResponse还包含userText,可以取到用户输入的内容。

10.<UserNotifications/UNNotificationSettings.h>

主要是通知的一些属性设置,对应属性,有三个枚举值

typedef NS_ENUM(NSInteger, UNAuthorizationStatus) {
     // The user has not yet made a choice regarding whether the application may post user notifications.
    UNAuthorizationStatusNotDetermined = 0,

    // The application is not authorized to post user notifications.
    UNAuthorizationStatusDenied,

    // The application is authorized to post user notifications.
    UNAuthorizationStatusAuthorized
} __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);

typedef NS_ENUM(NSInteger, UNNotificationSetting) {
    // The application does not support this notification type
    UNNotificationSettingNotSupported  = 0,

    // The notification setting is turned off.
     UNNotificationSettingDisabled,

    // The notification setting is turned on.
    UNNotificationSettingEnabled,
} __IOS_AVAILABLE(10.0) __TVOS_AVAILABLE(10.0) __WATCHOS_AVAILABLE(3.0);

typedef NS_ENUM(NSInteger, UNAlertStyle) {
    UNAlertStyleNone = 0,
    UNAlertStyleBanner,
    UNAlertStyleAlert,
} __IOS_AVAILABLE(10.0) __TVOS_PROHIBITED __WATCHOS_PROHIBITED;
11.<UserNotifications/UNNotificationSound.h>

两个方法,一个是设置为默认提示音,一个是设置为自定义提示音。
可以直接使用声音的name,而不是文件路径。

// The default sound used for notifications.
+ (instancetype)defaultSound;

// The name of a sound file to be played for the notification. The sound file must be contained in the app’s bundle or in the Library/Sounds folder of the app's data container. If files exist in both locations then the file in ~/Library/Sounds will be preferred.
+ (instancetype)soundNamed:(NSString *)name __WATCHOS_PROHIBITED;
12.<UserNotifications/UNNotificationTrigger.h>

NotificationTrigger算是新增的比较重要的功能,四种方式为App的运用提供了更多可选择的方法。其中,日历和地理位置是新增Trigger。

所以,此类下面有四个子类:push通知触发, 时间通知触发,日历通知触发,地区通知触发。与之相对应的,子类拥有自己出发的属性。

UNNotificationTrigger
13.<UserNotifications/UNUserNotificationCenter.h>

吐个槽,为毛和USNotificationCenter长得这么想,第一眼看差点蒙了。

最主要的类,通知的注册,激活,编辑,删除等功能都由该类完成。
通过[UNUserNotificationCenter currentNotificationCenter]单例方法进行操作。

14.<UserNotifications/UNNotificationServiceExtension.h>

里面有两个方法,收到通知的请求后调用, 系统将要销毁时调用。

15.<UserNotifications/UNNotificationContentExtension.h>

这个类不在UserNotifications.framework中,主要是用作修改通知展示的UI,使用时需要添加UserNotificationsUI.framework。
下文会做详细介绍。

Notification Extension

从Share Extension等开始,iOS逐渐支持一些利用沙盒等方法的扩展方法。在iOS 10中,与通知有关的有两个:Service ExtensionContent Extension
前者可以让我们有机会在收到远程推送的通知后,展示之前对通知内容进行自定义修改;后者可以用来自定义通知视图的UI样式。

Notification Extensions
iOS 10 中被标为弃用的 API
  • UILocalNotification
  • UIMutableUserNotificationAction
  • UIMutableUserNotificationCategory
  • UIUserNotificationAction
  • UIUserNotificationCategory
  • UIUserNotificationSettings
  • handleActionWithIdentifier:forLocalNotification:
  • handleActionWithIdentifier:forRemoteNotification:
  • didReceiveLocalNotification:withCompletion:
  • didReceiveRemoteNotification:withCompletion:

Demo

Demo地址:https://github.com/derekhuangxu/NotificationDemo

因为iOS 10更改了对于获取权限的限制,所以作为适配来说,希望大家在info.plist文件中更改对于权限的获取相关字段。

1、文字通知

文字通知相对简单,只是需要创建UNMutableNotificationContent实体类,创建UNTimeIntervalNotificationTrigger的实体类,然后加入到通知中心的单例中,然后触发即可。

- (IBAction)btnCreateWordNotofication:(id)sender {

    UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc]init];
    content.title = @"NotificationCenter";
    content.subtitle = @"testWord";
    content.body = @"本地通知测试";
    UNTimeIntervalNotificationTrigger * triger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5 repeats:NO];
    NSString * requestIdentifier = @"request";
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:requestIdentifier content:content trigger:triger];
    [[UNUserNotificationCenter currentNotificationCenter]addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
    
    }];
}

2、图片通知

与纯文字通知相似,但是需要额外在UNMutableNotificationContent中添加UNNotificationAttachment属性,剩下的基本相似。需要注意的是,content.attachments是一个数组。

- (IBAction)btnCreatePictrueNotofication:(id)sender {

    UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc]init];
    content.title = @"NotificationCenter";
    content.subtitle = @"testWord";
    content.body = @"本地通知测试";

    NSString * imagePath = [[NSBundle mainBundle]pathForResource:@"Notification_Image" ofType:@"png"];
    if (imagePath) {
        UNNotificationAttachment * imageAttachment = [UNNotificationAttachment attachmentWithIdentifier:@"imagePath"
                                                                                                    URL:[NSURL fileURLWithPath:imagePath]
                                                                                                options:nil
                                                                                                  error:nil];
        if (imageAttachment) {
            content.attachments = @[imageAttachment];
        }
    }

    UNTimeIntervalNotificationTrigger * triger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5 repeats:NO];
    NSString * requestIdentifier = @"request";
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:requestIdentifier content:content trigger:triger];
    [[UNUserNotificationCenter currentNotificationCenter]addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
    
    }];
}

3、视频通知

视频通知和图片通知的方法是同样的,但是在Demo中,我在视频通知的方法里面添加了3个Action,通过方法,添加到通知中心的单例即可,具体使用方法和作用上文已有介绍。

- (IBAction)btnCreateVideoNotofication:(id)sender {

    UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc]init];
    content.title = @"NotificationCenter";
    content.subtitle = @"testWord";
    content.body = @"本地通知测试";

    NSString * videoPath = [[NSBundle mainBundle]pathForResource:@"Notification_video" ofType:@"mp4"];
    if (videoPath) {
        UNNotificationAttachment * videoAttachment = [UNNotificationAttachment attachmentWithIdentifier:@"videoPath"
                                                                                                    URL:[NSURL fileURLWithPath:videoPath]
                                                                                                options:nil
                                                                                                  error:nil];
        if (videoAttachment) {
            content.attachments = @[videoAttachment];
        }
    }

    NSMutableArray * actionArr = [NSMutableArray array];
    UNNotificationAction * actionA = [UNNotificationAction actionWithIdentifier:@"IdentifierUnlock" title:@"进入应用" options:UNNotificationActionOptionForeground];
    UNNotificationAction * actionB = [UNNotificationAction actionWithIdentifier:@"IdentifierRed" title:@"Another Action" options:UNNotificationActionOptionDestructive];
    UNNotificationAction * actionC = [UNNotificationAction actionWithIdentifier:@"IdentifierBlack" title:@"Third Action" options:UNNotificationActionOptionAuthenticationRequired];

    [actionArr addObjectsFromArray:@[actionA, actionB, actionC]];
    if (actionArr.count > 0) {
        UNNotificationCategory * categoryNotification = [UNNotificationCategory categoryWithIdentifier:@"categoryOperationAction"
                                                                                               actions:actionArr
                                                                                     intentIdentifiers:@[]
                                                                                               options:UNNotificationCategoryOptionCustomDismissAction];
        [[UNUserNotificationCenter currentNotificationCenter]setNotificationCategories:[NSSet setWithObject:categoryNotification]];
        content.categoryIdentifier = @"categoryOperationAction";
    }

    UNTimeIntervalNotificationTrigger * triger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5 repeats:NO];
    NSString * requestIdentifier = @"request";
    UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:requestIdentifier content:content trigger:triger];
    [[UNUserNotificationCenter currentNotificationCenter]addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
    
    }];
}

最后

本文主要介绍了API的使用,简单书写了一个本地触发通知的简单Demo,后面会单研究一下关于远程推送多媒体通知和自定义UI通知。

前有Apple Watch,后面有新的widget和新的UserNotifications.framework,苹果似乎很努力的将我们沉浸在手机中的注意力解脱出来,极力避免我们被纷繁的通知和short action打扰而不得不打开App的可能性。

不知道这算不算Apple对于手机的使用又将有新的定义。

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容