iOS10推送通知进阶(Notification Extension)

2023-10-20 09:48

本文主要是介绍iOS10推送通知进阶(Notification Extension),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 
  • 简介
  • UNNotificationServiceExtension - 通知服务扩展
  • UNNotificationContentExtension - 通知内容扩展

<h2 id="简介"></h2>

简介

这篇文章主要讲iOS10推送通知的两个扩展框架:UNNotificationServiceExtension(通知服务扩展)UNNotificationContentExtension(通知内容扩展)。有关iOS10推送通知的新特性,请看这里

xcode-unnotification-extension.jpeg
  • UNNotificationServiceExtension(通知服务扩展)是在收到通知后,展示通知前,做一些事情的。比如,增加附件,网络请求等。
  • 想要给通知创建一个自定义的用户界面,需要 UNNotificationContentExtension(通知内容扩展)。

<h2 id="UNNotificationServiceExtension-通知服务扩展"></h2>

UNNotificationServiceExtension - 通知服务扩展

如果经常使用iMessage的朋友们,就会经常收到一些信息,附带了一些照片或者视频,所以推送中能附带这些多媒体是非常重要的。如果推送中包含了这些多媒体信息,可以使用户不用打开app,不用下载就可以快速浏览到内容。众所周知,推送通知中带了push payload,即使z去年苹果已经把payload的size提升到了4k bites,但是这么小的容量也无法使用户能发送一张高清的图片,甚至把这张图的缩略图包含在推送通知里面,也不一定放的下去。在iOS X中,我们可以使用新特性来解决这个问题。我们可以通过新的service extensions来解决这个问题。

iOS10给通知添加附件有两种情况:本地通知和远程通知。

  1. 本地推送通知,只需给content.attachments设置UNNotificationAttachment附件对象
  2. 远程推送通知,需要实现 UNNotificationServiceExtension(通知服务扩展),在回调方法中处理 推送内容时设置 request.content.attachments(请求内容的附件) 属性,之后调用 contentHandler 方法即可。

UNNotificationServiceExtension 提供在远程推送将要被 push 出来前,处理推送显示内容的机会。此时可以对通知的 request.content 进行内容添加,如添加附件,userInfo 等。下图显示了Notification Service Extension的流程:

unnotification-service-extension.jpg

处理的细节如下:

1.为了能在service extension 里面的attachment,必须给apns增加 "mutable-content":1 字段,使你的推送通知是动态可变的。

{"aps":{"alert":"Testing.. (34)","badge":1,"sound":"default","mutable-content":1}
}

2.给项目新建一个Notification Service Extension的扩展。

3.在-didReceiveNotificationRequest:withContentHandler:方法中处理request.content,用来给通知的内容做修改。如面代码示例了收到通知后,给通知增加图片附件:

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {self.contentHandler = contentHandler;self.bestAttemptContent = [request.content mutableCopy];self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];//1. 下载NSURL *url = [NSURL URLWithString:@"http://img1.gtimg.com/sports/pics/hv1/194/44/2136/138904814.jpg"];NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];NSURLSession *session = [NSURLSession sessionWithConfiguration:config];NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {if (!error) {//2. 保存数据NSString *path = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES).firstObjectstringByAppendingPathComponent:@"download/image.jpg"];UIImage *image = [UIImage imageWithData:data];NSError *err = nil;[UIImageJPEGRepresentation(image, 1) writeToFile:path options:NSAtomicWrite error:&err];//3. 添加附件UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:@"remote-atta1" URL:[NSURL fileURLWithPath:path] options:nil error:&err];if (attachment) {self.bestAttemptContent.attachments = @[attachment];}}//4. 返回新的通知内容self.contentHandler(self.bestAttemptContent);}];[task resume];
}

注意:使用UNNotificationServiceExtension,你有30秒的时间处理这个通知,可以同步下载图像和视频到本地,然后包装为一个UNNotificationAttachment扔给通知,这样就能展示用服务器获取的图像或者视频了。这里需要注意:如果数据处理失败,超时,extension会报一个崩溃信息,但是通知会用默认的形式展示出来,app不会崩溃。

附件通知所带的附件格式大小都是有限的,并不能做所有事情,视频的前几帧作为一个通知的附件是个不错的选择。

<h2 id="UNNotificationContentExtension-通知内容扩展"></h2>

UNNotificationContentExtension - 通知内容扩展

要想创建一个自定义的用户界面,需要用到Notification Content Extension(通知内容扩展)。

Notification Content Extension(通知内容扩展)允许开发者加入自定义的界面,在这个界面里面,你可以绘制任何你想要的东西。但是有一个最重要的限制就是,这个自定义的界面没有交互。它们不能接受点击事件,用户并不能点击它们。但是推送通知还是可以继续与用户进行交互,因为用户可以使用notificaiton的actions。注意:extension也可以处理这些actions

1. 推送界面的组成
unnotification-composition.png
  • header的UI是系统提供的一套标准的UI。这套UI会提供给所有的推送通知。
  • header下面的custom content是自定义的内容,就是Notification Content Extension(通知内容扩展)。在这里,就可以显示任何你想绘制的内容了。你可以展示任何额外的有用的信息给用户。
  • default content是系统的界面。这也就是iOS 9 之前的推送的样子。
  • 最下面的notification action,在这一段,用户可以触发一些操作。并且这些操作还会相应的反映到上面的自定义的推送界面content extension中。
2. 创建Notification Content Extension

创建一个新的Notification Content的target。Xcode自动生成一个新的模板,下面有三个文件,ViewController、main Interface storyboard、info.plist。

unnotification-content-extension-files.png

然后打开这里的ViewController。

#import "NotificationViewController.h"
#import <UserNotifications/UserNotifications.h>
#import <UserNotificationsUI/UserNotificationsUI.h>@interface NotificationViewController () <UNNotificationContentExtension>@property IBOutlet UILabel *label;@end@implementation NotificationViewController- (void)viewDidLoad {[super viewDidLoad];// Do any required interface initialization here.
}- (void)didReceiveNotification:(UNNotification *)notification {self.label.text = notification.request.content.body;
}@end

发现这里的ViewController就是一个普通的UIViewController, 但是它实现了UNNotificationContentExtension协议。

UNNotificationContentExtension协议有一个required方法didReceiveNotification:。当收到指定categroy的推送时, didReceiveNotification:方法会随着ViewController的生命周期方法,一起被调用,这样就能接受notification object,更新UI。

3. 配置category

接下来就是要让推送到达后,系统怎样找到自定义的UI。这时候就需要配置extension的info.plist文件。

unnotification-content-extension-info1.jpeg

这里和我们给notification actions注册category一样,给这个通知扩展指定相应的category。在UNNotificationExtensionCategory字段里写入相应的category id。值得提到的一点是,这里对应的category是可以为一个数组的,里面可以为多个category,这样做的目的是多个category共用同一套UI。

unnotification-content-extension-info2.jpeg

上图中category id为myNotificationCategory1和myNotificationCategory2的通知就共用了一套UI。

设置了category后, 只要在通知里面增加category字段,值是上面在extension的plist里面配置的category id, 收到的通知就会通过自定义的样式显示。

远程通知在apns里面增加category字段。

{"aps":{"alert":"Testing.. (34)","badge":1,"sound":"default","category":"myNotificationCategory1"}
}
4. 自定义UI

然后开始写自定义UI。

- (void)didReceiveNotification:(UNNotification *)notification {self.label.text = [NSString stringWithFormat:@"%@ [modified]", notification.request.content.title];self.subLabel.text = [NSString stringWithFormat:@"%@ [modified]", notification.request.content.body];self.imageView.image = [UIImage imageNamed:@"hong.png"];
}

可以在ViewController中增加一些Label和ImageView,收到通知的时候,提取想要的内容,或者添加额外的内容,设置到我们自定义的View上。

custom-ui1.png
5. 优化

优化一:发现是自定义界面的大小很不美观

  1. 这时候可以通过设置ViewController的preferredContentSize大小,控制自定义视图的大小。
  2. 也可以通过约束,控制自定义视图的大小。
- (void)viewDidLoad {[super viewDidLoad];self.preferredContentSize = CGSizeMake(CGRectGetWidth(self.view.frame), 100);
}

优化二:目标大小的问题解决了,但是发现视图恢复成正确的尺寸前,先展示有一大片空白的样子,然后变成正确的样子。当通知展示出来之后,它的大小并不是正常的我们想要的尺寸。iOS系统会去做一个动画来Resize它的大小。这样体验很差。

custom-ui2.gif

会出现上面这张图的原因是,在推送送达的那一刻,iOS系统需要知道我们推送界面的最终大小。但是我们自定义的extension在系统打算展示推送通知的那一刻,并还没有启动。所以这个时候,在我们代码都还没有跑起来之前,我们需要告诉iOS系统,我们的View最终要展示的大小。

为了解决这个问题,我们需要在extension的info.plist里设置一个content size ratio。增加字段UNNotificationExtensionInitialContentSizeRatio。

unnotification-content-extension-info3.jpeg

这个属性定义了宽和高的比例。当然设置了这个比例以后,也并不是万能的。因为你并不知道你会接受到多长的content。当你仅仅只设置比例,还是不能完整的展示所有的内容。有些时候如果我们可以知道最终的尺寸,那么我们固定尺寸会更好。

优化三:这时候我们发现我们自定义的界面显示的内容(custom content)和系统默认的内容(default content)重复了。

可以在extension的info.plist里设置,把系统默认的样式隐藏。增加字段UNNotificationExtensionDefaultContentHidden。

unnotification-content-extension-info4.jpeg

将系统内容隐藏后效果如下:

custom-ui3.png
6. 自定义操作

iOS8开始引入的action的工作原理:

默认系统的Action的处理是,当用户点击的按钮,就把action传递给app,与此同时,推送通知会立即消失。这种做法很方便。

但是有的情况是,希望用户点击action按钮后,效果及时响应在我们自定义的UI上。这个时候,用户点击完按钮,我们把这个action直接传递给extension,而不是传递给app。当actions传递给extension时,它可以延迟推送通知的消失时间。在这段延迟的时间之内,我们就可以处理用户点击按钮的事件了,并且更新UI,一切都处理完成之后,我们再去让推送通知消失掉。

这里我们可以运用UNNotificationContentExtension协议的第二个方法,这方法是Optional

- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption option))completion
{if ([response.actionIdentifier isEqualToString:@"action-like"]) {self.label.text = @"点赞成功~";}else if ([response.actionIdentifier isEqualToString:@"action-collect"]){self.label.text = @"收藏成功~";        }else if ([response.actionIdentifier isEqualToString:@"action-comment"]){self.label.text = [(UNTextInputNotificationResponse *)response userText];}//这里如果点击的action类型为UNNotificationActionOptionForeground,//则即使completion设置成Dismiss的,通知也不能消失dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{completion(UNNotificationContentExtensionResponseOptionDismiss);});
}

在这个方法里判断所有的action,更新界面,并延迟1.5秒后让通知消失。真实情况可能是,点击“赞”按钮后,发送请求给服务器,根据服务器返回结果,展示不同的UI效果在通知界面上,然后消失。如果是评论,则将评论内容更新到界面上。

如果还想把这个action传递给app,最后消失的参数应该这样:

completion(UNNotificationContentExtensionResponseOptionDismissAndForwardAction);

但是我实际运行遇见这种情况,如果点击的action类型为UNNotificationActionOptionForeground,则即使completion设置成Dismiss的,通知也不能消失,也没有启动app,这里不太明白为什么会这样,如果有知道原因的,欢迎大家评论。

7.自定义输入型操作

action 有2种类型:

  • UNNotificationAction 普通按钮样式
  • UNTextInputNotificationAction 输入框样式

UNTextInputNotificationAction的样式如下:

custom-ui4.png

系统的输入样式的action,只有在点击发送按钮时,才能接受到action的响应回调。(比如上面的didReceiveNotificationResponse:completionHandler:方法)。但有的时候系统的样式或者功能不能满足需求,这时候可以自定义键盘上面的inputAccessoryView。

首先,重写ViewController的下面两个方法:

- (BOOL)canBecomeFirstResponder
{return YES;
}- (UIView *)inputAccessoryView
{return self.customInputView;
}

自定义inputAccessoryView,以绘制自定义的输入样式。

- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption option))completion
{...}else if ([response.actionIdentifier isEqualToString:@"action-comment"]){self.label.text = [(UNTextInputNotificationResponse *)response userText];[self becomeFirstResponder];[self.textField becomeFirstResponder];self.completion = completion;}
}

实现了点击评论按钮,ViewController 成为第一响应者,使自定义的输入样式显示出来。然后,让textField成为第一响应者,使键盘弹出。

这里将操作的completion保存,以便在需要的时候调用。比如,可以在点击键盘右下的send按钮时,调用completion,使通知消失。

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{[textField resignFirstResponder];self.label.text = textField.text;dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{self.completion(UNNotificationContentExtensionResponseOptionDismiss);});return YES;
}

实现效果入下:

custom-ui5.png

结合使用两个扩展

可以在content extension里面绘制界面时,通过notification.request.content.attachments获取附件放到自定义控件里面。

- (void)didReceiveNotification:(UNNotification *)notification {...UNNotificationAttachment *attachment = notification.request.content.attachments.firstObject;if (attachment) {if ([attachment.URL startAccessingSecurityScopedResource]) {self.imageView.image = [UIImage imageWithContentsOfFile:attachment.URL.path];dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{[attachment.URL stopAccessingSecurityScopedResource];});}}
}

我们可以提取content的attachments。前文提到过,attachment是由系统管理的,系统会把它们单独的管理,这意味着它们存储在我们sandbox之外。所以这里我们要使用attachment之前,我们需要告诉iOS系统,我们需要使用它,并且在使用完毕之后告诉系统我们使用完毕了。对应上述代码就是-startAccessingSecurityScopedResource和-stopAccessingSecurityScopedResource的操作。当我们获取到了attachment的使用权之后,我们就可以使用那个文件获取我们想要的信息了。

关于调试

很多人在开发 iOS extension 时遇到了调试的问题,可以看这里的解决方法,如果还不能有效解决您的问题,欢迎评论留言。

结束语

更多代码实现请查看Demo, 如发现问题,请帮忙指正。

参考
  • iOS10 User Notifications 学习笔记
  • 【WWDC2016 Session】iOS 10 推送Notification新特性


作者:liuyanhongwl
链接:https://www.jianshu.com/p/78ef7bc04655
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这篇关于iOS10推送通知进阶(Notification Extension)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/246305

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

[MySQL表的增删改查-进阶]

🌈个人主页:努力学编程’ ⛅个人推荐: c语言从初阶到进阶 JavaEE详解 数据结构 ⚡学好数据结构,刷题刻不容缓:点击一起刷题 🌙心灵鸡汤:总有人要赢,为什么不能是我呢 💻💻💻数据库约束 🔭🔭🔭约束类型 not null: 指示某列不能存储 NULL 值unique: 保证某列的每行必须有唯一的值default: 规定没有给列赋值时的默认值.primary key:

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。

从0到1,AI我来了- (7)AI应用-ComfyUI-II(进阶)

上篇comfyUI 入门 ,了解了TA是个啥,这篇,我们通过ComfyUI 及其相关Lora 模型,生成一些更惊艳的图片。这篇主要了解这些内容:         1、哪里获取模型?         2、实践如何画一个美女?         3、附录:               1)相关SD(稳定扩散模型的组成部分)               2)模型放置目录(重要)

java学习,进阶,提升

http://how2j.cn/k/hutool/hutool-brief/1930.html?p=73689

【408DS算法题】039进阶-判断图中路径是否存在

Index 题目分析实现总结 题目 对于给定的图G,设计函数实现判断G中是否含有从start结点到stop结点的路径。 分析实现 对于图的路径的存在性判断,有两种做法:(本文的实现均基于邻接矩阵存储方式的图) 1.图的BFS BFS的思路相对比较直观——从起始结点出发进行层次遍历,遍历过程中遇到结点i就表示存在路径start->i,故只需判断每个结点i是否就是stop

【Python从入门到进阶】64、Pandas如何实现数据的Concat合并

接上篇《63.Pandas如何实现数据的Merge》 上一篇我们学习了Pandas如何实现数据的Merge,本篇我们来继续学习Pandas如何实现数据的Concat合并。 一、引言 在数据处理过程中,经常需要将多个数据集合并为一个统一的数据集,以便进行进一步的分析或建模。这种需求在多种场景下都非常常见,比如合并不同来源的数据集以获取更全面的信息、将时间序列数据按时间顺序拼接起来以观察长期趋势等

【Linux 从基础到进阶】 Python脚本在运维中的应用

Python脚本在运维中的应用 在现代运维工作中,Python因其简洁、高效和跨平台的特性,成为了系统管理员自动化工作的重要工具。Python不仅可以轻松处理系统管理任务,还可以与各种运维工具进行无缝集成。本文将介绍Python脚本在运维中的常见应用场景,帮助运维人员提高效率,减少人工操作错误。 1. 自动化任务调度 示例介绍 运维工作中,定时任务是非常常见的需求。虽然cron是Linux