iOS10通知框架UserNotification理解与应用
一、引言
关于通知,无论与远程push还是本地通知,以往的ios系统暴漏给开发者的接口都是十分有限的,开发者只能对标题和内容进行简单的定义,至于ui展示和用户交互行为相关的部分,开发者开发起来都十分困难。至于本地通知,ios10之前采用的是uilocationnotification类,远程通知有苹果服务器进行转发,本地通知和远程通知其回调的处理都是通过appdelegate中的几个回调方法来完成。ios10系统中,通知功能的增强是一大优化之处,ios10中将通知功能整合成了一个框架usernotification,其结构十分类似于ios8中的uiwebview向webkit框架整合的思路。并且usernotification相比之前的通知功能更加强大,主要表现在如下几点:
1.通知处理代码可以从appdelegate中剥离。
2.通知的注册,设置,处理更加结构化,更易于模块化开发。
3.usernotification支持自定义通知音效和启动图。
4.usernotification支持向通知内容中添加媒体附件,例如音频,视频。
5.usernotification支持开发者定义多套通知模板。
6.usernotification支持完全自定义的通知界面。
7.usernotification支持自定义通知中的用户交互按钮。
8.通知的触发更加容易管理。
从上面列举的几点就可以看出,ios10中的usrenotification真的是一个大的改进,温故而知新,关于ios之前版本本地通知和远程通知的相关内容请查看如下博客:
本地推送:。
远程推送:。
二、usernotification概览
学习一个新的框架或知识模块时,宏观上了解其体系,大体上掌握其结构是十分必要的,这更有利于我们对这个框架或模块的整体把握与理解。usernotification框架中拆分定义了许多类、枚举和结构体,其中还定义了许多常量,类与类之间虽然关系复杂,但脉络十分清晰,把握住主线,层层分析,边很容易理解和应用usernotification框架。
下图中列举了usernotification框架中所有核心的类:
如图中关系所示,usernotification框架中的核心类列举如下:
unnotificationcenter:通知管理中心,单例,通知的注册,接收通知后的回调处理等,是usernotification框架的核心。
unnotification:通知对象,其中封装了通知请求。
unnotificationsettings:通知相关设置。
unnotificationcategory:通知模板。
unnotificationaction:用于定义通知模板中的用户交互行为。
unnotificationrequest:注册通知请求,其中定义了通知的内容和触发方式。
unnotificationresponse:接收到通知后的回执。
unnotificationcontent:通知的具体内容。
unnotificationtrigger:通知的触发器,由其子类具体定义。
unnotificationattachment:通知附件类,为通知内容添加媒体附件。
unnotificationsound:定义通知音效。
unpushnotificationtrigger:远程通知的触发器,unnotificationtrigger子类。
untimeinervalnotificationtrigger:计时通知的触发器,unnotificationtrigger子类。
uncalendarnotificationtrigger:周期通知的触发器,unnotificationtrigger子类。
unlocationnotificationtrigger:地域通知的触发器,unnotificationtrigger子类。
unnotificationcenterdelegate:协议,其中方法用于监听通知状态。
三、进行通知用户权限申请与创建普通的本地通知
要在ios系统中使用通知,必须获取到用户权限,usernotification框架中申请通知用户权限需要通过unnotificationcenter来完成,示例如下:
//进行用户权限的申请 [[unusernotificationcenter currentnotificationcenter] requestauthorizationwithoptions:unauthorizationoptionbadge|unauthorizationoptionsound|unauthorizationoptionalert|unauthorizationoptioncarplay completionhandler:^(bool granted, nserror * _nullable error) { //在block中会传入布尔值granted,表示用户是否同意 if (granted) { //如果用户权限申请成功,设置通知中心的代理 [unusernotificationcenter currentnotificationcenter].delegate = self; } }];
申请用户权限的方法中需要传入一个权限内容的参数,其枚举定义如下:
typedef ns_options(nsuinteger, unauthorizationoptions) { //允许更新app上的通知数字 unauthorizationoptionbadge = (1 << 0), //允许通知声音 unauthorizationoptionsound = (1 << 1), //允许通知弹出警告 unauthorizationoptionalert = (1 << 2), //允许车载设备接收通知 unauthorizationoptioncarplay = (1 << 3), };
获取到用户权限后,使用usernotification创建普通的通知,示例代码如下:
//通知内容类 unmutablenotificationcontent * content = [unmutablenotificationcontent new]; //设置通知请求发送时 app图标上显示的数字 content.badge = @2; //设置通知的内容 content.body = @"这是ios10的新通知内容:普通的ios通知"; //默认的通知提示音 content.sound = [unnotificationsound defaultsound]; //设置通知的副标题 content.subtitle = @"这里是副标题"; //设置通知的标题 content.title = @"这里是通知的标题"; //设置从通知激活app时的launchimage图片 content.launchimagename = @"lun"; //设置5s之后执行 untimeintervalnotificationtrigger * trigger = [untimeintervalnotificationtrigger triggerwithtimeinterval:5 repeats:no]; unnotificationrequest * request = [unnotificationrequest requestwithidentifier:@"notificationdefault" content:content trigger:trigger]; //添加通知请求 [[unusernotificationcenter currentnotificationcenter] addnotificationrequest:request withcompletionhandler:^(nserror * _nullable error) { }];
效果如下面图示:
四、通知音效类unnotificationsound
通知可以进行自定义的音效设置,其中方法如下:
//系统默认的音效 + (instancetype)defaultsound; //自定义的音频音效 /* 注意,音频文件必须在bundle中或者在library/sounds目录下 */ + (instancetype)soundnamed:(nsstring *)name __watchos_prohibited;
五、通知触发器unnotificationtrigger
通知触发器可以理解为定义通知的发送时间,unnotificationtrigger是触发器的基类,具体的触发器由它的四个子类实现,实际上,开发者在代码中可能会用到的触发器只有三种,unpushnotificationtrigger远程推送触发器开发者不需要创建使用,远程通知有远程服务器触发,开发者只需要创建与本地通知有关的触发器进行使用。
1.untimeintervalnotificationtrigger
untimeintervalnotificationtrigger是计时触发器,开发者可以设置其在添加通知请求后一定时间发送。
//创建触发器 在timeinterval秒后触发 可以设置是否循环触发 + (instancetype)triggerwithtimeinterval:(nstimeinterval)timeinterval repeats:(bool)repeats; //获取下次触发的时间点 - (nullable nsdate *)nexttriggerdate;
2.uncalendarnotificationtrigger
uncalendarnotificationtrigger是日历触发器,开发者可以设置其在某个时间点触发。
//创建触发器 设置触发时间 可以设置是否循环触发 + (instancetype)triggerwithdatematchingcomponents:(nsdatecomponents *)datecomponents repeats:(bool)repeats; //下一次触发的时间点 - (nullable nsdate *)nexttriggerdate;
3.unlocationnotificationtrigger
unlocationnotificationtrigger是地域触发器,开发者可以设置当用户进入某一区域时触发。
//地域信息 @property (ns_nonatomic_iosonly, readonly, copy) clregion *region; //创建触发器 + (instancetype)triggerwithregion:(clregion *)region repeats:(bool)repeats __watchos_prohibited;
六、为通知内容添加附件
附件主要指的是媒体附件,例如图片,音频和视频,为通知内容添加附件需要使用unnotificationattachment类。示例代码如下:
//创建图片附件 unnotificationattachment * attach = [unnotificationattachment attachmentwithidentifier:@"imageattach" url:[nsurl fileurlwithpath:[[nsbundle mainbundle] pathforresource:@"2" oftype:@"jpg"]] options:nil error:nil]; unmutablenotificationcontent * content = [unmutablenotificationcontent new]; //设置附件数组 content.attachments = @[attach]; content.badge = @1; content.body = @"这是ios10的新通知内容:普通的ios通知"; //默认的通知提示音 content.sound = [unnotificationsound defaultsound]; content.subtitle = @"这里是副标题"; content.title = @"这里是通知的标题"; //设置5s之后执行 untimeintervalnotificationtrigger * trigger = [untimeintervalnotificationtrigger triggerwithtimeinterval:5 repeats:no]; unnotificationrequest * request = [unnotificationrequest requestwithidentifier:@"notificationdefaultimage" content:content trigger:trigger]; [[unusernotificationcenter currentnotificationcenter] addnotificationrequest:request withcompletionhandler:^(nserror * _nullable error) { }];
效果如下图
需要注意,unnotificationcontent的附件数组虽然是一个数组,但是系统的通知模板只能展示其中的第一个附件,设置多个附件也不会有额外的效果,但是如果开发者进行通知模板ui的自定义,则此数组就可以派上用场了。音频附件界面如下:
需要注意,添加附件的格式和大小都有一定的要求,如下表格所示:
创建通知内容附件unnotificationattachment实例的方法中有一个options配置字典,这个字典中可以进行配置的键值对如下:
//配置附件的类型的键 需要设置为nsstring类型的值,如果不设置 则默认从扩展名中推断 extern nsstring * const unnotificationattachmentoptionstypehintkey __ios_available(10.0) __watchos_available(3.0); //配置是否隐藏缩略图的键 需要配置为nsnumber 0或者1 extern nsstring * const unnotificationattachmentoptionsthumbnailhiddenkey __ios_available(10.0) __watchos_available(3.0); //配置使用一个标准的矩形来对缩略图进行裁剪,需要配置为cgrectcreatedictionaryrepresentation(cgrect)创建的矩形引用 extern nsstring * const unnotificationattachmentoptionsthumbnailclippingrectkey __ios_available(10.0) __watchos_available(3.0); //使用视频中的某一帧作为缩略图 配置为nsnumber时间 extern nsstring * const unnotificationattachmentoptionsthumbnailtimekey __ios_available(10.0) __watchos_available(3.0);
七、定义通知模板unnotificationcategory
聊天类软件在ios系统中,常常采用后台推送的方式推送新消息,用户可以在不进入应用程序的情况下,直接在左面回复通知推送过来的信息,这种功能就是通过unnotificationcategory模板与unnotificationaction用户活动来实现的。关于文本回复框,usernotification框架中提供了untextinputnotificationaction类,其是unnotificationaction的子类。示例代码如下:
//创建用户活动 /* options参数可选如下: //需要在解开锁屏下使用 unnotificationactionoptionauthenticationrequired //是否指示有破坏性 unnotificationactionoptiondestructive //是否允许活动在后台启动app unnotificationactionoptionforeground //无设置 unnotificationactionoptionnone */ untextinputnotificationaction * action = [untextinputnotificationaction actionwithidentifier:@"action" title:@"回复" options:unnotificationactionoptionauthenticationrequired textinputbuttontitle:@"活动" textinputplaceholder:@"请输入回复内容"]; //创建通知模板 unnotificationcategory * category = [unnotificationcategory categorywithidentifier:@"mynotificationcategorytext" actions:@[action] intentidentifiers:@[] options:unnotificationcategoryoptioncustomdismissaction]; unmutablenotificationcontent * content = [unmutablenotificationcontent new]; content.badge = @1; content.body = @"这是ios10的新通知内容:普通的ios通知"; //默认的通知提示音 content.sound = [unnotificationsound defaultsound]; content.subtitle = @"这里是副标题"; content.title = @"这里是通知的标题"; //设置通知内容对应的模板 需要注意 这里的值要与对应模板id一致 content.categoryidentifier = @"mynotificationcategorytext"; //设置5s之后执行 untimeintervalnotificationtrigger * trigger = [untimeintervalnotificationtrigger triggerwithtimeinterval:5 repeats:no]; [[unusernotificationcenter currentnotificationcenter] setnotificationcategories:[nsset setwithobjects:category, nil]]; unnotificationrequest * request = [unnotificationrequest requestwithidentifier:@"notificationdefaulttext" content:content trigger:trigger]; [[unusernotificationcenter currentnotificationcenter] addnotificationrequest:request withcompletionhandler:^(nserror * _nullable error) { }];
需要注意,要使用模板,通知内容unnotificationcontent的categoryidentifier要与unnotificationcategory的id一致。效果如下:
也可以为通知模板添加多个自定义的用户交互按钮,示例如下:
unnotificationaction * action = [unnotificationaction actionwithidentifier:@"action" title:@"活动标题1" options:unnotificationactionoptionnone]; unnotificationaction * action2 = [unnotificationaction actionwithidentifier:@"action" title:@"活动标题2" options:unnotificationactionoptionnone]; unnotificationaction * action3 = [unnotificationaction actionwithidentifier:@"action" title:@"活动标题3" options:unnotificationactionoptionnone]; unnotificationaction * action4 = [unnotificationaction actionwithidentifier:@"action" title:@"活动标题4" options:unnotificationactionoptionnone]; unnotificationcategory * category = [unnotificationcategory categorywithidentifier:@"mynotificationcategorybtn" actions:@[action,action2,action3,action4] intentidentifiers:@[] options:unnotificationcategoryoptioncustomdismissaction]; unmutablenotificationcontent * content = [unmutablenotificationcontent new]; content.badge = @1; content.body = @"这是ios10的新通知内容:普通的ios通知"; //默认的通知提示音 content.sound = [unnotificationsound defaultsound]; content.subtitle = @"这里是副标题"; content.title = @"这里是通知的标题"; content.categoryidentifier = @"mynotificationcategorybtn"; //设置5s之后执行 untimeintervalnotificationtrigger * trigger = [untimeintervalnotificationtrigger triggerwithtimeinterval:5 repeats:no]; unnotificationrequest * request = [unnotificationrequest requestwithidentifier:@"notificationdefault" content:content trigger:trigger]; [[unusernotificationcenter currentnotificationcenter] setnotificationcategories:[nsset setwithobjects:category, nil]]; [[unusernotificationcenter currentnotificationcenter] addnotificationrequest:request withcompletionhandler:^(nserror * _nullable error) { }];
需要注意,系统模板最多支持添加4个用户交互按钮,如下图:
八、自定义通知模板ui
通过前边的介绍,我们发现通过usernotification框架开发者已经可以完成许多从来很难实现的效果。然而这都不是usernotification框架最强大的地方,usernotification框架最强大的地方在于其可以完全自定义通知的ui界面。
完全自定义通知界面是通过ios扩展来实现的,首先创建一个新的target,如下图:
选择notification content,如下:
创建完成后,会发现工程中多了一个notification content的扩展,其中自带一个storyboard文件和一个notificationviewcontroller类,开发者可以在storyboard文件或者直接在controller类中进行自定义界面的编写。
需要注意,notificationviewcontroller自动遵守了unnotificationcontentextension协议,这个协议专门用来处理自定义通知ui的内容展示,其中方法列举如下:
//接收到通知时会被调用 /* 开发者可以从notification对象中拿到附件等内容进行ui刷新 */ - (void)didreceivenotification:(unnotification *)notification; //当用户点击了通知中的用户交互按钮时会被调用 /* response对象中有通知内容相关信息 在回调block块completion中,开发者可以传入一个unnotificationcontentextensionresponseoption参数来告诉系统如何处理这次用户活动 unnotificationcontentextensionresponseoption枚举中可选值如下: typedef ns_enum(nsuinteger, unnotificationcontentextensionresponseoption) { //不关闭当前通知界面 unnotificationcontentextensionresponseoptiondonotdismiss, //关闭当前通知界面 unnotificationcontentextensionresponseoptiondismiss, //关闭当前通知界面并将用户活动传递给宿主app处理 unnotificationcontentextensionresponseoptiondismissandforwardaction, } __ios_available(10_0) __tvos_unavailable __watchos_unavailable __osx_unavailable; */ - (void)didreceivenotificationresponse:(unnotificationresponse *)response completionhandler:(void (^)(unnotificationcontentextensionresponseoption option))completion; /* 这个属性作为get方法进行实现 这个方法用来返回一个通知界面要显示的媒体按钮 typedef ns_enum(nsuinteger, unnotificationcontentextensionmediaplaypausebuttontype) { //不显示媒体按钮 unnotificationcontentextensionmediaplaypausebuttontypenone, //默认的媒体按钮 当点击按钮后 进行播放与暂停的切换 按钮始终显示 unnotificationcontentextensionmediaplaypausebuttontypedefault, //overlay风格 当点击按钮后,媒体播放,按钮隐藏 点击媒体后,播放暂停,按钮显示。 unnotificationcontentextensionmediaplaypausebuttontypeoverlay, } __ios_available(10_0) __tvos_unavailable __watchos_unavailable __osx_unavailable; */ @property (nonatomic, readonly, assign) unnotificationcontentextensionmediaplaypausebuttontype mediaplaypausebuttontype; //返回媒体按钮的位置 @property (nonatomic, readonly, assign) cgrect mediaplaypausebuttonframe; //返回媒体按钮的颜色 @property (nonatomic, readonly, copy) uicolor *mediaplaypausebuttontintcolor; //点击播放按钮的回调 - (void)mediaplay; //点击暂停按钮的回调 - (void)mediapause; //媒体开始播放的回调 - (void)mediaplayingstarted __ios_available(10_0) __tvos_unavailable __watchos_unavailable __osx_unavailable; //媒体开始暂停的回调 - (void)mediaplayingpaused __ios_available(10_0) __tvos_unavailable __watchos_unavailable __osx_unavailable;
需要注意,自定义的通知界面上虽然可以放按钮,可以放任何ui控件,但是其不能进行用户交互,唯一可以进行用户交互的方式是通过协议中的媒体按钮及其回调方法。
定义好了通知ui模板,若要进行使用,还需要再notification content扩展中的info.plist文件的nsextension字典的nsextensionattributes字典里进行一些配置,正常情况下,开发者需要进行配置的键有3个,分别如下:
unnotificationextensioncategory:设置模板的categoryid,用于与unnotificationcontent对应。
unnotificationextensioninitialcontentsizeratio:设置自定义通知界面的高度与宽度的比,宽度为固定宽度,在不同设备上有差别,开发者需要根据宽度计算出高度进行设置,系统根据这个比值来计算通知界面的高度。
unnotificationextensiondefaultcontenthidden:是有隐藏系统默认的通知界面。
配置info.plist文件如下:
用如下的代码创建通知:
unnotificationaction * action = [unnotificationaction actionwithidentifier:@"action" title:@"活动标题1" options:unnotificationactionoptionnone]; //根据id拿到自定义ui的模板 unnotificationcategory * category = [unnotificationcategory categorywithidentifier:@"mynotificationcategoryh" actions:@[action] intentidentifiers:@[] options:unnotificationcategoryoptioncustomdismissaction]; unmutablenotificationcontent * content = [unmutablenotificationcontent new]; content.badge = @1; content.body = @"这是ios10的新通知内容:普通的ios通知"; //默认的通知提示音 content.sound = [unnotificationsound defaultsound]; content.subtitle = @"这里是副标题"; content.title = @"这里是通知的标题"; content.categoryidentifier = @"mynotificationcategoryh"; //设置5s之后执行 untimeintervalnotificationtrigger * trigger = [untimeintervalnotificationtrigger triggerwithtimeinterval:5 repeats:no]; unnotificationrequest * request = [unnotificationrequest requestwithidentifier:@"notificationdefaultcustomuih" content:content trigger:trigger]; [[unusernotificationcenter currentnotificationcenter] setnotificationcategories:[nsset setwithobjects:category, nil]]; [[unusernotificationcenter currentnotificationcenter] addnotificationrequest:request withcompletionhandler:^(nserror * _nullable error) { }];
效果如下图:
如果将unnotificationextensiondefaultcontenthidden键值设置为0或者不设置,则不会隐藏系统默认的ui,如下:
九、通知回调的处理
usernotification框架对于通知的回调处理,是通过unusernotificationcenterdelegate协议来实现的,这个协议中有两个方法,如下:
/* 这个方法在应用在前台,并且将要弹出通知时被调用,后台状态下弹通知不会调用这个方法 这个方法中的block块completionhandler()可以传入一个unnotificationpresentationoptions类型的枚举 有个这个参数,开发者可以设置在前台状态下,依然可以弹出通知消息,枚举如下: typedef ns_options(nsuinteger, unnotificationpresentationoptions) { //只修改app图标的消息数 unnotificationpresentationoptionbadge = (1 << 0), //只提示通知音效 unnotificationpresentationoptionsound = (1 << 1), //只弹出通知框 unnotificationpresentationoptionalert = (1 << 2), } __ios_available(10.0) __tvos_available(10.0) __watchos_available(3.0); //什么都不做 static const unnotificationpresentationoptions unnotificationpresentationoptionnone */ - (void)usernotificationcenter:(unusernotificationcenter *)center willpresentnotification:(unnotification *)notification withcompletionhandler:(void (^)(unnotificationpresentationoptions options))completionhandler __ios_available(10.0) __tvos_available(10.0) __watchos_available(3.0); /* 这个方法当接收到通知后,用户点击通知激活app时被调用,无论前台还是后台 */ - (void)usernotificationcenter:(unusernotificationcenter *)center didreceivenotificationresponse:(unnotificationresponse *)response withcompletionhandler:(void(^)())completionhandler __ios_available(10.0) __watchos_available(3.0) __tvos_prohibited;
十、usernotification框架中其他零散知识
前面所介绍的内容基本涵盖了usernotification框架中所有的内容,在以后的应用开发中,开发者可以在通知方面发挥更大的想象力与创造力,给用户更加友好的体验。除了前边所介绍过的核心内容外,usernotification框架中还有一些零散的类、枚举等。
1.错误码描述
typedef ns_enum(nsinteger, unerrorcode) { //通知不被允许 unerrorcodenotificationsnotallowed = 1, //附件无效url unerrorcodeattachmentinvalidurl = 100, //附件类型错误 unerrorcodeattachmentunrecognizedtype, //附件大小错误 unerrorcodeattachmentinvalidfilesize, //附件数据错误 unerrorcodeattachmentnotindatastore, unerrorcodeattachmentmoveintodatastorefailed, unerrorcodeattachmentcorrupt, //时间无效 unerrorcodenotificationinvalidnodate = 1400, //无内容 unerrorcodenotificationinvalidnocontent, } __ios_available(10.0) __tvos_available(10.0) __watchos_available(3.0);
2.unnotification类
@interface unnotification : nsobject <nscopying, nssecurecoding> //触发的时间 @property (nonatomic, readonly, copy) nsdate *date; //内置的通知请求对象 @property (nonatomic, readonly, copy) unnotificationrequest *request; - (instancetype)init ns_unavailable; @end
3.unnotificationsettings类
unnotificationsettings类主要用来获取与通知相关的信息。
@interface unnotificationsettings : nsobject <nscopying, nssecurecoding> //用户权限状态 @property (ns_nonatomic_iosonly, readonly) unauthorizationstatus authorizationstatus; //音效设置 @property (ns_nonatomic_iosonly, readonly) unnotificationsetting soundsetting __tvos_prohibited; //图标提醒设置 @property (ns_nonatomic_iosonly, readonly) unnotificationsetting badgesetting __watchos_prohibited; //提醒框设置 @property (ns_nonatomic_iosonly, readonly) unnotificationsetting alertsetting __tvos_prohibited; //通知中心设置 @property (ns_nonatomic_iosonly, readonly) unnotificationsetting notificationcentersetting __tvos_prohibited; //锁屏设置 @property (ns_nonatomic_iosonly, readonly) unnotificationsetting lockscreensetting __tvos_prohibited __watchos_prohibited; //车载设备设置 @property (ns_nonatomic_iosonly, readonly) unnotificationsetting carplaysetting __tvos_prohibited __watchos_prohibited; //提醒框风格 @property (ns_nonatomic_iosonly, readonly) unalertstyle alertstyle __tvos_prohibited __watchos_prohibited; @end
unnotificationsetting枚举如下:
typedef ns_enum(nsinteger, unnotificationsetting) { //不支持 unnotificationsettingnotsupported = 0, //不可用 unnotificationsettingdisabled, //可用 unnotificationsettingenabled, }
unauthorizationstatus枚举如下:
typedef ns_enum(nsinteger, unauthorizationstatus) { //为做选择 unauthorizationstatusnotdetermined = 0, // 用户拒绝 unauthorizationstatusdenied, // 用户允许 unauthorizationstatusauthorized }
unalertstyle枚举如下:
typedef ns_enum(nsinteger, unalertstyle) { //无 unalertstylenone = 0, //顶部banner样式 unalertstylebanner, //警告框样式 unalertstylealert, }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。