OC学习68:本地通知UserNotifications Framework

张建 lol

前言

通知和推送 是应用程序中很重要的组成部分。本地通知 可以为应用程序注册一些定时任务,例如闹钟、定时提醒等。远程推送 则更强大,提供了一种通过服务端主动推送消息到客户端的方式,服务端可以更加灵活地控制通知逻辑,例如广告的推送、定时任务的提醒、即时通信类应用离线消息的提醒等。本文先着重着介绍本地通知,由于iOS系统的不断更新,本地通知的API也需要根据设备的系统来进行选择和兼容。

  • iOS10 之前,开发者需要使用 UILocalNotification 类来实现本地通知;

  • iOS10 之后,苹果为了加强对通知和推送的统一管理,提高通知界面的高可定制性,引入了UserNotification 框架。

注:本地通知不需要连接网络,一般是开发人员在合适的情况下,在App内发送通知。

UILocalNotification

  1. 简介

ULLocalNotificationiOS8 中的一个类(In iOS 8.0 and later),用来实现本地通知功能。通知,实际上是由iOS系统管理的一个功能,比如注册了通知,则系统会在通知被触发时给应用程序发送消息。但是,ULLocalNotification仅能提供开发者去编辑消息,消息推送到app上展示的样式和交互则是固定的,开发者自定制的难度相当大。

  1. 添加步骤
  • 创建通知对象
  • 设置触发时间
  • 设置通知属性
  • 执行本地通知
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 推送本地通知 iOS 10.0 及以下
- (void)pushLocalNotificationIOS10AndBelowWithTime:(int)time
title:(NSString *)title
body:(NSString *)body
repeats:(BOOL)repeats
identifier:(NSString *)identifier{
// 1.创建通知对象
UILocalNotification * notification = [[UILocalNotification alloc] init];

// 2.设置触发通知的时间(几秒后发送)
NSDate * fireDate = [NSDate dateWithTimeIntervalSinceNow:time];
notification.fireDate = fireDate;

// 3.设置通知属性
// 通知标题
notification.alertTitle = title;
// 通知主体
notification.alertBody = body;
// 待机界面开启左滑按钮
notification.hasAction = YES;
// 待机界面的滑动按钮提示
notification.alertAction = @"点击查看消息";
// 在收到通知时播放的声音,默认消息声音
notification.soundName = UILocalNotificationDefaultSoundName;
// 设置启动屏
// notification.alertLaunchImage = @"";
// 传递的用户数据
// notification.userInfo = @{};
// 图标的消息数
// notification.applicationIconBadgeNumber = 1;
// 时区
// notification.timeZone = [NSTimeZone defaultTimeZone];
// 设置重复的间隔
// notification.repeatInterval = kCFCalendarUnitSecond;

// 附加操作
// notification.category = @"choose";

// 4.执行本地通知
[[UIApplication sharedApplication] scheduleLocalNotification:notification];

}

立即执行本地通知用:

1

  1. 处理逻辑
  • 申请通知授权
1
2
3
4
5
6
7
8
9
// 本地通知授权
- (void)localNotificationAuthorizationIOS10AndBelow{
// 如果已经得到授权,就直接添加本地通知,否则申请询问授权
if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) {
// 开始授权
UIUserNotificationSettings * notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeSound categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
}
}
  • 推送本地通知
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 推送本地通知 iOS 10.0 及以下
- (void)pushLocalNotificationIOS10AndBelowWithTime:(int)time
title:(NSString *)title
body:(NSString *)body
repeats:(BOOL)repeats
identifier:(NSString *)identifier{
if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) {
[SVPManager showWithStatus:@"请您在前往 设置->通用->零信任->通知->开启允许通知 权限,以便为您提供本地通知服务" time:2];
return;
}

// 1.创建通知对象
UILocalNotification * notification = [[UILocalNotification alloc] init];

// 2.设置触发通知的时间(几秒后发送)
NSDate * fireDate = [NSDate dateWithTimeIntervalSinceNow:time];
notification.fireDate = fireDate;

// 3.设置通知属性
// 通知标题
notification.alertTitle = title;
// 通知主体
notification.alertBody = body;
// 待机界面开启左滑按钮
notification.hasAction = YES;
// 待机界面的滑动按钮提示
notification.alertAction = @"点击查看消息";
// 在收到通知时播放的声音,默认消息声音
notification.soundName = UILocalNotificationDefaultSoundName;
// 设置启动屏
// notification.alertLaunchImage = @"";
// 传递的用户数据
// notification.userInfo = @{};
// 图标的消息数
// notification.applicationIconBadgeNumber = 1;
// 时区
// notification.timeZone = [NSTimeZone defaultTimeZone];
// 设置重复的间隔
// notification.repeatInterval = kCFCalendarUnitSecond;

// 附加操作
// notification.category = @"choose";

// 4.执行本地通知
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
  • 收到本地通知处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
App处于kill掉时,点击通知栏进入App,使用该方法接收本地(远程)通知
*/
- (void)receiveLocalNotificationWithLaunchOptions:(NSDictionary *)launchOptions{
/*
App被kill掉,通过通知启动App
1.本地的 key - UIApplicationLaunchOptionsLocalNotificationKey
2.远程的 key - UIApplicationLaunchOptionsRemoteNotificationKey
*/
if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) {
UILocalNotification * notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
[self handleLocalNotificationWithWithNotification:notification userInfo:launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]];
}
}
// 处理本地通知
- (void)handleLocalNotificationWithWithNotification:(UILocalNotification *)notification userInfo:(NSDictionary *)userInfo{
NSLog(@"title:%@ body:%@",notification.alertTitle,notification.alertBody);
}

AppDelegate下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#pragma mark - 接收本地通知
/*
App处于挂起或运行时,调用该方法接收本地(远程)通知
1.当App在前台状态下,点击推送通知,会调用该方法
2.当App在后台状态下,点击推送通知,会调用该方法(从锁屏界面点击推送通知也会执行)
*/
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
NSLog(@"%@",notification);
// 前台点击通知消息
if (application.applicationState == UIApplicationStateActive) {
NSLog(@"处理前台接受通知的业务");
}
// 后台点击通知消息
else {
NSLog(@"处理后台台接受通知的业务");
[[LocalNotiManager sharedManager] handleLocalNotificationWithWithNotification:notification userInfo:notification.userInfo];
}
}
// 当用户点击允许或者不允许时,会执行如下代理方法,我们在其中实现处理逻辑
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings{
NSLog(@"当用户点击允许或者不允许时,会执行如下代理方法");
}
  1. 附加操作

可以添加附加操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 添加附加操作
- (void)addCategoryAction{
if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {
/*
推送通知的附加操作,可以为nil,此时只显示消息
如果不为nil,可以再推送消息的后面增加几个按钮(如同意、不同意)
*/
UIMutableUserNotificationCategory * category = [UIMutableUserNotificationCategory new];
category.identifier = @"choose";

// 同意
UIMutableUserNotificationAction * action1 = [UIMutableUserNotificationAction new];
action1.identifier = @"yes";
action1.title = @"同意";
// 点击按钮是否进入前台
action1.activationMode = UIUserNotificationActivationModeForeground;
action1.authenticationRequired = true;
action1.destructive = false;

// 不同意
UIMutableUserNotificationAction * action2 = [UIMutableUserNotificationAction new];
action2.identifier = @"no";
action2.title = @"不同意";
// 后台模式
action2.activationMode = UIUserNotificationActivationModeBackground;
action2.authenticationRequired = true;
action2.destructive = true;

if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) {
action2.behavior = UIUserNotificationActionBehaviorTextInput;
action2.parameters = @{UIUserNotificationTextInputActionButtonTitleKey:@"拒绝原因"};
}

[category setActions:@[action1,action2] forContext:UIUserNotificationActionContextDefault];

NSSet<UIUserNotificationCategory *> * categorys = [NSSet setWithObjects:category, nil];

UIUserNotificationType type = UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIUserNotificationTypeSound;
UIUserNotificationSettings * settings = [UIUserNotificationSettings settingsForTypes:type categories:categorys];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}
}


UserNotification

  1. 简介

UserNotification 是iOS10后苹果提出的一个整合的通知和推送框架,对之前的通知和推送功能进行了全面的重构和优化,功能更强大,定制更灵活。表现如下:

  • 通知处理代码从AppDelegate中剥离
  • 通知的注册、设置、处理更加结构化,更易于进行模块的开发
  • 支持自定义通知音效和启动图
  • 支持向通知内容中添加媒体附件,例如音效、视频
  • 支持开发者定义多套通知展示模块
  • 支持完全自定义的通知界面
  • 支持自定义通知中的用户交互按钮
  • 通知的触发更加容易管理
  1. 核心类结构图

  • UNNotificationCenter:通知管理中心单例设计,负责通知的注册、接收通知后的回调处理等,是UserNofitication框架的核心。
  • UNNotification:通知对象,其中封装了通知请求
  • UNNoticationSettings:通知相关设置
  • UNNotificationCategory:通知模板
  • UNNotificationAction:用于定义通知模板中的用户交互行为
  • UNNotificationRequest:注册通知请求,其中定义了通知的内容和触发方式
  • UNNotificationResponse:接收到通知后的回执
  • UNNotificationContent:通知的具体内容
  • UNNotificationAttachment:通知所携带的附件,为通知内容添加
  • UNNotificationSound:定义通知音效, (音频文件必须位于bundle或者Library/Sounds目录下)
  • UNNotificationTrigger:通知触发器,由其子类具体定义
  • UNPushNotificationTrigger:远程推送触发器,UNNotificationTrigger的子类
  • UNTimerInrevalNotificationTrigger:计时器触发器,UNNotificationTrigger的子类
  • UNCalendarNotificationTrigger:周期日历触发器,UNNotificationTrigger的子类
  • UNLocationNotificationTrigger:地域触发器,UNNotificationTrigger的子类
  • UNNotificationCenterDelegate:协议,其中方法用于监听通知状态

注意:

  • 媒体附件大小

  • 对于收到的附件通知,可以把消息下拉看到完整的附件内容(见下面的代码示例图所展示的样子)

  • 内容附件实例中options配置字典键/值作用,本示例代码中options默认置为nil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
extern Nsstring *const UNNotificationAttachmentoptionsTypeHintkey:
配置附件的类型的键,NSString类型的值,如果不设置,则默认从扩展名推断

extern NSstring *const
UNNotificationAttachmentoptionsThumbnailHiddenkey:
配置是否隐藏缩路图,配置NSNumber类型的值 0或者1

CGRectCreateDictionaryRepresentation(CGRect):
创建的矩形引用

extern NSString *cost UNNotificationAttachmentOptionsThumbnailClippingRectKey:
创建使用一个标准的矩形对缩略图进行裁剪

extern NSString *const UNNotificationAttachmentoptionsThumbnailTimekey:
使用视频中的某一帧作为缩略图,配置NSNumber类型的时间
  • 附件资源放置位置Bundle目录下

  1. 权限申请 和 附件资源包位置(Bundle目录下)
  • 权限申请
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 本地通知授权
- (void)localNotificationAuthorizationIOS10AndAbove{
// 使用 UNUserNotificationCenter 来管理通知
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
// 设置通知中心的代理
center.delegate = self;

// iOS 10.0使用以下方法注册,才能得到授权
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
// 用户是否同意
if (granted) {
NSLog(@"用户同意授权");
}else {
NSLog(@"用户不同意授权");
}
}];
}
  1. 推送本地通知
  • 普通通知
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 推送本地通知 iOS 10.0 及以上
- (void)pushLocalNotificationIOS10AndAboveWithTime:(int)time
title:(NSString *)title
body:(NSString *)body
repeats:(BOOL)repeats
identifier:(NSString *)identifier{
// 1.创建通知管理类
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];

// 2.设置通知内容
UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc] init];
// 设置通知请求发送时App图标上显示的数字
// content.badge = @1;
// 设置通知的标题
content.title = [NSString localizedUserNotificationStringForKey:title arguments:nil];
// 设置通知的副标题
// content.subtitle = @"";
// 设置通知的主体
content.body = [NSString localizedUserNotificationStringForKey:body arguments:nil];
// 设置通知的提示音
content.sound = [UNNotificationSound defaultSound];
// 设置从通知激活App时的launchImage图片
// content.launchImageName = @"";

/* 3.设置触发器: 计时器触发
1.TimeInterval:时间间隔
2.repeats:是否重复*/
UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:time repeats:repeats];

/* 设置触发器: 周期日历触发 */
// NSDateComponents * components = [[NSDateComponents alloc] init];
// components.year = 2023;
// components.month = 11;
// components.day = 2;
// UNCalendarNotificationTrigger * trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:repeats];

/* 设置触发器:地域触发 */
// CLRegion * region = [[CLCircularRegion alloc] initWithCenter:CLLocationCoordinate2DMake(33.0, 11.0) radius:100 identifier:@"region"];
// UNLocationNotificationTrigger * trigger = [UNLocationNotificationTrigger triggerWithRegion:region repeats:repeats];

/*
4.设置通知请求
1.Identifier:通知标识
2.content:通知内容
3.trigger:触发器
*/
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];

// 5.添加通知请求
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"添加推送通知失败");
}else {
NSLog(@"添加推送通知成功");
}
}];
}
  • 图片通知
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 推送本地通知 iOS 10.0 及以上(图片)
- (void)pushLocalNotificationIOS10AndAboveWithTime:(int)time
imgName:(NSString *)imgName
title:(NSString *)title
body:(NSString *)body
repeats:(BOOL)repeats
identifier:(NSString *)identifier{
// 1.创建通知管理类
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];

// 2.通知内容
UNMutableNotificationContent * content = [UNMutableNotificationContent new];

/* 设置图片附件
attachments:数组,但系统的通知模板只能展示其中一个附件,设置多个附件也不会有额外的效果
但是如果开发者自定义通知模板UI,此数组就派上用场了
*/
NSString * path = [[NSBundle mainBundle] pathForResource:imgName ofType:nil];
NSURL * url = [NSURL fileURLWithPath:path];
UNNotificationAttachment * attachment = [UNNotificationAttachment attachmentWithIdentifier:identifier URL:url options:nil error:nil];
content.attachments = @[attachment];

// 设置通知的标题
content.title = [NSString localizedUserNotificationStringForKey:title arguments:nil];
// 设置通知的副标题
// content.subtitle = @"";
// 设置通知请求时发送时App图标上显示的数字
// content.badge = @1;
// 设置通知的主体
content.body = body;
// 设置通知提示音
content.sound = [UNNotificationSound defaultSound];
// 设置从通知激活App时的launchImage图片
// content.launchImageName = @"";

/* 4.设置触发器: 计时器触发
1.TimeInterval:时间间隔
2.repeats:是否重复*/
UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:time repeats:repeats];

/*
4.设置通知请求
1.Identifier:通知标识
2.content:通知内容
3.trigger:触发器
*/
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];

// 5.添加通知请求
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"添加推送通知失败");
}else {
NSLog(@"添加推送通知成功");
}
}];
}
  • 音频通知
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 推送本地通知 iOS 10.0 及以上(音频)
- (void)pushLocalNotificationIOS10AndAboveWithAudioName:(NSString *)audioName
time:(int)time
title:(NSString *)title
body:(NSString *)body
repeats:(BOOL)repeats
identifier:(NSString *)identifier{
// 1.创建通知管理类
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];

// 2.通知内容
UNMutableNotificationContent * content = [UNMutableNotificationContent new];

/* 设置音频附件
attachments:数组,但系统的通知模板只能展示其中一个附件,设置多个附件也不会有额外的效果
但是如果开发者自定义通知模板UI,此数组就派上用场了
*/
NSString * path = [[NSBundle mainBundle] pathForResource:audioName ofType:nil];
NSURL * url = [NSURL fileURLWithPath:path];
UNNotificationAttachment * attachment = [UNNotificationAttachment attachmentWithIdentifier:identifier URL:url options:nil error:nil];
content.attachments = @[attachment];

// 设置通知的标题
content.title = [NSString localizedUserNotificationStringForKey:title arguments:nil];
// 设置通知的副标题
// content.subtitle = @"";
// 设置通知请求时发送时App图标上显示的数字
// content.badge = @1;
// 设置通知的主体
content.body = body;
// 设置通知提示音
content.sound = [UNNotificationSound defaultSound];
// 设置从通知激活App时的launchImage图片
// content.launchImageName = @"";

/* 4.设置触发器: 计时器触发
1.TimeInterval:时间间隔
2.repeats:是否重复*/
UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:time repeats:repeats];

/*
4.设置通知请求
1.Identifier:通知标识
2.content:通知内容
3.trigger:触发器
*/
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];

// 5.添加通知请求
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"添加推送通知失败");
}else {
NSLog(@"添加推送通知成功");
}
}];
}
  • 视频通知
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 推送本地通知 iOS 10.0 及以上(视频)
- (void)pushLocalNotificationIOS10AndAboveWithVideoName:(NSString *)videoName
time:(int)time
title:(NSString *)title
body:(NSString *)body
repeats:(BOOL)repeats
identifier:(NSString *)identifier{
// 1.创建通知管理类
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];

// 2.通知内容
UNMutableNotificationContent * content = [UNMutableNotificationContent new];

/* 设置音频附件
attachments:数组,但系统的通知模板只能展示其中一个附件,设置多个附件也不会有额外的效果
但是如果开发者自定义通知模板UI,此数组就派上用场了
*/
NSString * path = [[NSBundle mainBundle] pathForResource:videoName ofType:nil];
NSURL * url = [NSURL fileURLWithPath:path];
UNNotificationAttachment * attachment = [UNNotificationAttachment attachmentWithIdentifier:identifier URL:url options:nil error:nil];
content.attachments = @[attachment];

// 设置通知的标题
content.title = [NSString localizedUserNotificationStringForKey:title arguments:nil];
// 设置通知的副标题
// content.subtitle = @"";
// 设置通知请求时发送时App图标上显示的数字
// content.badge = @1;
// 设置通知的主体
content.body = body;
// 设置通知提示音
content.sound = [UNNotificationSound defaultSound];
// 设置从通知激活App时的launchImage图片
// content.launchImageName = @"";

/* 4.设置触发器: 计时器触发
1.TimeInterval:时间间隔
2.repeats:是否重复*/
UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:time repeats:repeats];

/*
4.设置通知请求
1.Identifier:通知标识
2.content:通知内容
3.trigger:触发器
*/
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];

// 5.添加通知请求
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"添加推送通知失败");
}else {
NSLog(@"添加推送通知成功");
}
}];
}
  1. 效果

  1. 使用模板

除了上面介绍的强大的附件通知外,我们还可以把 UserNotification 提供的模板功能和用户行为利用起来。在iOS系统中,聊天类软件常常采用后台推送的方式推送消息,用户可以在不进入应用程序的情况下,直接在桌面回复通过通知推送过来的消息,这种功能就是通过 UNNotificationCategoryUNNotificationAction 用户行为来实现的。对于文本回复框,UserNotification 框架提供了 UNTextInputNotificationAction 类,也即 UNNotificationAction 的子类。

  • UNTextInputNotificationAction 创建文本回复框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// 在桌面对本地通知进行回复
- (void)replyLocalNotificationAtDesktopWithInputIdentifier:(NSString *)inputIdentifier
inputTitle:(NSString *)inputTitle
inputBtnTitle:(NSString *)inputBtnTitle
inputPlaceholder:(NSString *)inputPlaceholder
categoryIdentifier:(NSString *)categoryIdentifier
title:(NSString *)title
body:(NSString *)body
time:(int)time
repeats:(BOOL)repeats
inentifier:(NSString *)identifier
{
// 创建管理类
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];

// 创建回复框
UNTextInputNotificationAction * inputAction = [UNTextInputNotificationAction actionWithIdentifier:inputIdentifier title:inputIdentifier options:UNNotificationActionOptionAuthenticationRequired textInputButtonTitle:inputBtnTitle textInputPlaceholder:inputPlaceholder];

// 创建通知模板
UNNotificationCategory * category = [UNNotificationCategory categoryWithIdentifier:categoryIdentifier actions:@[inputAction] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];

// 设置通知内容
UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc] init];
// 设置通知请求发送时App图标上显示的数字
// content.badge = @1;
// 设置通知的标题
content.title = [NSString localizedUserNotificationStringForKey:title arguments:nil];
// 设置通知的副标题
// content.subtitle = @"";
// 设置通知的主体
content.body = [NSString localizedUserNotificationStringForKey:body arguments:nil];
// 设置通知的提示音
content.sound = [UNNotificationSound defaultSound];
// 设置从通知激活App时的launchImage图片
// content.launchImageName = @"";

// 设置通知模板(categoryIdentifier和上面的保持一致)
content.categoryIdentifier = categoryIdentifier;
[center setNotificationCategories:[NSSet setWithObjects:category, nil]];

/* 设置触发器: 计时器触发
1.TimeInterval:时间间隔
2.repeats:是否重复*/
UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:time repeats:repeats];

/*
设置通知请求
1.Identifier:通知标识
2.content:通知内容
3.trigger:触发器
*/
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];

// 添加通知请求
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"添加推送通知失败");
}else {
NSLog(@"添加推送通知成功");
}
}];

}

  • UNNotificationAction创建用户交互按钮
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// 在桌面对本地通知进行按钮交互
// 在桌面对本地通知进行按钮交互
- (void)supportLocationNotificationUserInterfaceButton{

// 创建通知管理类
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];

/*
创建交互按钮(系统模板最多支持添加4个交互按钮)
UNNotificationActionOptionNone: 无设置
*/
UNNotificationAction * action1 = [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:@"myNotificationCategoryButton" actions:@[action1,action2,action3,action4] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];

// 通知内容
UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc] init];
// 设置通知请求发送时App图标上显示的数字
// content.badge = @1;
// 设置通知的标题
content.title = [NSString localizedUserNotificationStringForKey:title arguments:nil];
// 设置通知的副标题
// content.subtitle = @"";
// 设置通知的主体
content.body = [NSString localizedUserNotificationStringForKey:body arguments:nil];
// 设置通知的提示音
content.sound = [UNNotificationSound defaultSound];
// 设置从通知激活App时的launchImage图片
// content.launchImageName = @"";

// 设置通知模板(categoryIdentifier和上面的保持一致)
content.categoryIdentifier = categoryIdentifier;
[center setNotificationCategories:[NSSet setWithObjects:category, nil]];

/* 设置触发器: 计时器触发
1.TimeInterval:时间间隔
2.repeats:是否重复*/
UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:time repeats:repeats];

/*
设置通知请求
1.Identifier:通知标识
2.content:通知内容
3.trigger:触发器
*/
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];

// 添加通知请求
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"添加推送通知失败");
}else {
NSLog(@"添加推送通知成功");
}
}];
}

  1. 通知扩展

通过 UserNotification 框架,开发者已经可以完成从前很难实现的效果。然后这都不是这个框架最强大的地方,它的最强大的功能是通过扩展实现 完全自定义的通过UI界面。也即 Notification Content Extension。在项目新建一个 Target 后,然后选择 Notification Content Extension 扩展文件并创建,此时这个扩展文件自带了一个故事板 storyBoard 和一个 NotificationViewCenter 类,开发者可以在 storyBoard 中或者 NotificationViewCenter 中直接定制需要的UI界面即可,具体方法可以去看API。需要注意的是,NotificationViewCenter 类自动遵守了 UNNotificationContentExtension 协议,这个协议专门用来处理自定义的通知UI的内容展示。

注意:

在自定义的的通知界面上,虽然可以放置按钮或者任何UI控件,但其不能进行用户交互,唯一可以进行交互的方式是通过协议中的媒体按钮及其回调方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 当用户点击通知中的用户交互按钮时会调用,开发者可以从notification对象中拿到附件等内容进行UI刷新
- (void)didReceiveNotification:(UNNotification *)notification;
- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption option))completion;

// 返回媒体按钮位置
@property (nonatomic, readonly, assign) CGRect mediaPlayPauseButtonFrame;

// 返回媒体按钮颜色
@property (nonatomic, readonly, copy) UIColor *mediaPlayPauseButtonTintColor;

// 点击播放和暂停播放按钮的回调
- (void)mediaPlay;
- (void)mediaPause;

// 打开和关闭通知的回调
- (void)performNotificationDefaultAction;
- (void)dismissNotificationContentExtension

// 媒体开始播放和暂停的回调
- (void)mediaPlayingStarted;
- (void)mediaPlayingPaused .
  • 创建扩展

  • 配置plist

在这个 NSExtensionAttributes 字典下,我们有三个属性可以添加

(1)UNNotificationExtensionCategory(必须要有,系统已经创建好)

解释:对应这个key的值,可以是字符串,也可以是一个数组,每一个字符串都是一个identifier,这个identifier对应着每一个 UNMutableNotificationContentcategoryIdentifier 的属性。

简单来说,就是在收到通知的时候,我们可以让服务器把这个通知的 categoryIdentifier 带上,作用是,我们可以根据视频、音乐、图片,来分别自定义我们的通知内容。不同的分类标识符,也会在我们讲到 UNNotificationAction 的时候,帮助我们区分是什么类型的通知,方便我们对不同类型的通知做出不同的操作行为。上面的截图中,我是一个字符串的形式。下图为数组形式:

这里设置categoryIdentifier,最好让服务器的推送内容带上这个,然后我们好更加的定制化。不建议本地写死。

(2)UNNotificationExtensionInitialContentSizeRatio(必须要有,系统已经创建好)

解释:这个值的类型是一个浮点类型,代表的是 高度与宽度的比值。系统会使用这个比值,作为初始化view的大小。举个简单的例子来说,如果该值为 1,则该视图为 正方形。如果为 0.5,则代表 高度是宽度的一半

注意:这个值只是初始化的一个值,在这个扩展添加后,可以重写frame,展示的时候,在我们还没打开这个视图预览时,背景是个类似图片占位的灰色,那个灰色的高度宽度之比,就是通过这个值来设定。

(3)UNNotificationExtensionDefaultContentHidden(可选)

解释:这个值是一个BOOL值,当为YES时,会隐藏上方原本推送的内容视图,只会显示我们自定义的视图。(因为在自定义视图的时候,我们可以取得推送内容,然后按照我们想要的布局,展示出来)如果为NO时(默认为NO),推送视图就会既有我们的自定义视图,也会有系统原本的推送内容视图(这里附件是不会显示的,只会显示body里面的文字哟)

(4)至于 NSExtensionMainStoryboard 以及 NSExtensionPointIdentifier,系统默认生成,大家直接用就好,如果需要更改的,只能更改使用的 storyboard 的名字(不过应该没人会把系统的删除在建立一个吧

MainInterface.storyboard:

这个就是个简单的storyboard文件,内部有一个View,这个View就是在上面的图层中的自定义View视图了。它与NotificationViewController所绑定。

NotificationViewController:

这是是系统帮我们默认创建了一个控制器,继承UIViewController,其实就是一个控制器啦。

  • 定制界面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//  NotificationViewController.m

// MyNotificationContentExtension

#import "NotificationViewController.h"

#import <UserNotifications/UserNotifications.h>

#import <UserNotificationsUI/UserNotificationsUI.h>

@interface NotificationViewController () <UNNotificationContentExtension>

@property (nonatomic, strong) UILabel *customTitleLabel1;

@property (nonatomic, strong) UILabel *customTitleLabel2;

@property (nonatomic, strong) UIImageView *customImageView1;

@property (nonatomic, strong) UIImageView *customImageView2;

@end

@implementation NotificationViewController

- (void)viewDidLoad {

[super viewDidLoad];

//屏幕宽

CGFloat screen_width = [UIScreen mainScreen].bounds.size.width;

self.view.backgroundColor = [UIColor redColor];

//自定义Label

self.customTitleLabel1 = [[UILabel alloc] initWithFrame:CGRectMake(, , screen_width, )];

self.customTitleLabel1.textColor = [UIColor whiteColor];

self.customTitleLabel1.textAlignment = NSTextAlignmentCenter;

self.customTitleLabel1.backgroundColor = [UIColor greenColor];

//自定义UIImageView

self.customImageView1 = [[UIImageView alloc] initWithFrame:CGRectMake(, , screen_width/, )];

self.customImageView2 = [[UIImageView alloc] initWithFrame:CGRectMake(screen_width/, , screen_width/, )];

self.customImageView1.backgroundColor = [UIColor purpleColor];

self.customImageView2.backgroundColor = [UIColor blueColor];

//自定义Label

self.customTitleLabel2 = [[UILabel alloc] initWithFrame:CGRectMake(, CGRectGetMaxY(self.customImageView1.frame)+, screen_width, )];

self.customTitleLabel2.textColor = [UIColor whiteColor];

self.customTitleLabel2.textAlignment = NSTextAlignmentCenter;

self.customTitleLabel2.backgroundColor = [UIColor orangeColor];

//添加控件

[self.view addSubview:self.customTitleLabel1];

[self.view addSubview:self.customTitleLabel2];

[self.view addSubview:self.customImageView1];

[self.view addSubview:self.customImageView2];

}

/*
收到通知时触发,但是这个是退出进程之后才使用,只适用于远程推送(所以本地推送,这两个方法是不会执行的)
拿到推送通知内容,刷新自定义的UI
*/
- (void)didReceiveNotification:(UNNotification *)notification {
NSLog(@"notification---------%@",notification);

}

// 用户交互时触发
- (void)didReceiveNotificationResponse:(UNNotificationResponse *)response completionHandler:(void (^)(UNNotificationContentExtensionResponseOption option))completion {
NSLog(@"response----------%@",response);
}

@end
  • 模板使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 支持完全自定义UI的通知
- (void)supportCustomUILocationNotification {

// 创建交互按钮
UNNotificationAction * action = [UNNotificationAction actionWithIdentifier:@"action" title:@"自定义的Action" options:UNNotificationActionOptionNone];

// 创建通知模板
// "myNotificationCategory" 要与 plist 中配置的保持一样
UNNotificationCategory * category = [UNNotificationCategory categoryWithIdentifier:@"myNotificationCategory" actions:@[action] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];

// 通知内容类
UNMutableNotificationContent * content = [UNMutableNotificationContent new];
// 设置通知请求发送时APP图标上显示的数字
content.badge = @1;
// 设置通知的内容
content.body = @"iOS10新通知内容,普通通知,欢迎哥来了";
// 设置通知提示音
content.sound = [UNNotificationSound defaultSound];
// 设置通知的副标题
content.subtitle = @"这是通知副标题";
// 设置通知的标题
content.title = @"这是通知标题";
// 设置从通知激活App时的lanunchImage图片
content.launchImageName = @"lun";

// 设置通知模板
// categoryIdentifier要与上面创建category的标识保持一致
content.categoryIdentifier = @"myNotificationCategory";
[[UNUserNotificationCenter currentNotificationCenter] setNotificationCategories:[NSSet setWithObjects:category, nil]];

// 设置触发器
// 计时器触发器
UNTimeIntervalNotificationTrigger * timrTrigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval: repeats:NO];

// 设置通知请求
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:@"UNNotificationCustomUIH" content:content trigger:timrTrigger];

// 添加通知请求
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (!error) {
NSLog(@"");
}
}];
}

  1. 重写媒体按钮
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#pragma mark - 重写媒体按钮
// 重写媒体按钮的frame
- (CGRect)mediaPlayPauseButtonFrame {
return CGRectMake(, , , );
}

// 重写媒体按钮的颜色
- (UIColor *)mediaPlayPauseButtonTintColor {
return [UIColor yellowColor];
}

// 重写媒体按钮类型
- (UNNotificationContentExtensionMediaPlayPauseButtonType)mediaPlayPauseButtonType {
return UNNotificationContentExtensionMediaPlayPauseButtonTypeDefault;
}

// 接收媒体按钮播放事件
- (void)mediaPlay {
NSLog(@"mediaPlay---------------开始播放");
}

// 接收媒体按钮暂停事件
- (void)mediaPause {
NSLog(@"mediaPause---------------暂停播放");
}

  1. 通知的代理方法

UserNotification框架对于通知的回调处理,是通过UNNotificationCenterDelegate协议来实现的。代理方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#pragma mark - UNUserNotificationCenterDelegate
/*
App在前台时,才会调用此方法:
如果未实现该方法或未及时调用该处理程序,则不会显示该通知。
应用程序可以选择将通知显示为声音,徽章,警报和/或显示在通知列表中。
该决定应基于通知中的信息是否对用户可见。
*/
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{
NSLog(@"App在前台,收到了通知");
// 处理通知
UNNotificationContent * content = notification.request.content;
if (content) {
NSLog(@"title:%@ body:%@",content.title,content.body);
}
/* 处理完成后,调用 completionHandler,用于指示在前台显示通知的形式
UNNotificationPresentationOptionNone:不在前台显示
UNNotificationPresentationOptionAlert:在前台显示
...
*/
completionHandler(UNNotificationPresentationOptionNone);
}

/*
当接收到通知后,在用户点击通知激活应用程序时调用这个方法,无论是在前台还是后台
*/
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler {
NSLog(@"App无论是在前台还是后台,收到了通知,用户点击该通知");
// 如果有内容
UNNotificationContent * content = response.notification.request.content;
if (content) {
NSLog(@"title:%@ body:%@",content.title,content.body);
}
}

总结

  1. 将上述代码封装到一个管理类 LocalNofiManager 中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/
// LocalNotificationManager.m
// JiAnBaoStandard
//
// Created by Mac on 2023/8/10.
// Copyright © 2023 JITPlatform. All rights reserved.
//

#import "LocalNotiManager.h"

@interface LocalNotiManager ()<UNUserNotificationCenterDelegate>
@end
@implementation LocalNotiManager
// 单例
+ (instancetype)sharedManager{
static LocalNotiManager * manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[self alloc] init];
});
return manager;
}

#pragma mark - 本地通知授权
- (void)localNotificationAuthorization{
if (@available(iOS 10.0, *)) {
[self localNotificationAuthorizationIOS10AndAbove];
}else{
[self localNotificationAuthorizationIOS10AndBelow];
}
}

#pragma mark - 推送本地通知
- (void)pushLocalNotificationWithTime:(int)time
title:(NSString *)title
body:(NSString *)body
repeats:(BOOL)repeats
identifier:(NSString *)identifier{
if (@available(iOS 10.0, *)) {
[self pushLocalNotificationIOS10AndAboveWithTime:time
title:title
body:body
repeats:repeats
identifier:identifier];
}else{
[self pushLocalNotificationIOS10AndBelowWithTime:time
title:title
body:body
repeats:repeats
identifier:identifier];
}
}

#pragma mark - iOS 10.0 及以上
// 本地通知授权
- (void)localNotificationAuthorizationIOS10AndAbove{
// 使用 UNUserNotificationCenter 来管理通知
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
// 设置通知中心的代理
center.delegate = self;

// iOS 10.0使用以下方法注册,才能得到授权
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionBadge + UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
// 用户是否同意
if (granted) {
NSLog(@"用户同意授权");
}else {
NSLog(@"用户不同意授权");
}
}];
}

// 推送本地通知 iOS 10.0 及以上(普通)
- (void)pushLocalNotificationIOS10AndAboveWithTime:(int)time
title:(NSString *)title
body:(NSString *)body
repeats:(BOOL)repeats
identifier:(NSString *)identifier{
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionBadge + UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!granted) {
dispatch_async(dispatch_get_main_queue(), ^{
[SVPManager showWithStatus:@"请您在前往 设置->通用->零信任->通知->开启允许通知 权限,以便为您提供本地通知服务" time:2.5];
});
return;
};
}];

// 1.创建通知管理类
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];

// 2.设置通知内容
UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc] init];
// 设置通知请求发送时App图标上显示的数字
// content.badge = @1;
// 设置通知的标题
content.title = [NSString localizedUserNotificationStringForKey:title arguments:nil];
// 设置通知的副标题
// content.subtitle = @"";
// 设置通知的主体
content.body = [NSString localizedUserNotificationStringForKey:body arguments:nil];
// 设置通知的提示音
content.sound = [UNNotificationSound defaultSound];
// 设置从通知激活App时的launchImage图片
// content.launchImageName = @"";

/* 3.设置触发器: 计时器触发
1.TimeInterval:时间间隔
2.repeats:是否重复*/
UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:time repeats:repeats];

/* 设置触发器: 周期日历触发 */
// NSDateComponents * components = [[NSDateComponents alloc] init];
// components.year = 2023;
// components.month = 11;
// components.day = 2;
// UNCalendarNotificationTrigger * trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:components repeats:repeats];

/* 设置触发器:地域触发 */
// CLRegion * region = [[CLCircularRegion alloc] initWithCenter:CLLocationCoordinate2DMake(33.0, 11.0) radius:100 identifier:@"region"];
// UNLocationNotificationTrigger * trigger = [UNLocationNotificationTrigger triggerWithRegion:region repeats:repeats];

/*
4.设置通知请求
1.Identifier:通知标识
2.content:通知内容
3.trigger:触发器
*/
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];

// 5.添加通知请求
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"添加推送通知请求失败");
}else {
NSLog(@"添加推送通知请求成功");
}
}];
}

#pragma mark - UNUserNotificationCenterDelegate
/*
App在前台时,才会调用此方法:
1.如果未实现该方法或未及时调用该处理程序,则不会显示该通知。
2.应用程序可以选择将通知显示为声音,徽章,警报和/或显示在通知列表中。
3.该决定应基于通知中的信息是否对用户可见。
*/
-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler{
NSLog(@"App在前台,收到了通知");
// 处理通知
UNNotificationContent * content = notification.request.content;
if (content) {
NSLog(@"title:%@ body:%@",content.title,content.body);
}
/* 处理完成后,调用 completionHandler,用于指示在前台显示通知的形式
UNNotificationPresentationOptionNone:不在前台显示
UNNotificationPresentationOptionAlert:在前台显示
...
*/
completionHandler(UNNotificationPresentationOptionNone);
}
/*
当接收到通知后,在用户点击通知激活App时调用这个方法(无论是在前台还是后台)
*/
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler{
NSLog(@"App无论是在前台还是后台,收到了通知,用户点击该通知");
// 如果有内容
UNNotificationContent * content = response.notification.request.content;
if (content) {
NSLog(@"title:%@ body:%@",content.title,content.body);
}
}

#pragma mark - iOS 10.0 及以下
// 本地通知授权
- (void)localNotificationAuthorizationIOS10AndBelow{
// 如果没有授权,则申请授权
if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) {
// 开始授权
UIUserNotificationSettings * notificationSettings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeSound | UIUserNotificationTypeBadge categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
}
}

// 推送本地通知 iOS 10.0 及以下
- (void)pushLocalNotificationIOS10AndBelowWithTime:(int)time
title:(NSString *)title
body:(NSString *)body
repeats:(BOOL)repeats
identifier:(NSString *)identifier{
if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) {
[SVPManager showWithStatus:@"请您在前往 设置->通用->零信任->通知->开启允许通知 权限,以便为您提供本地通知服务" time:2];
return;
}

// 1.创建通知对象
UILocalNotification * notification = [[UILocalNotification alloc] init];

// 2.设置触发通知的时间(几秒后发送)
NSDate * fireDate = [NSDate dateWithTimeIntervalSinceNow:time];
notification.fireDate = fireDate;

// 3.设置通知属性
// 通知标题
notification.alertTitle = title;
// 通知主体
notification.alertBody = body;
// 待机界面开启左滑按钮
notification.hasAction = YES;
// 待机界面的滑动按钮提示
notification.alertAction = @"点击查看消息";
// 在收到通知时播放的声音,默认消息声音
notification.soundName = UILocalNotificationDefaultSoundName;
// 设置启动屏
// notification.alertLaunchImage = @"";
// 传递的用户数据
// notification.userInfo = @{};
// 图标的消息数
// notification.applicationIconBadgeNumber = 1;
// 时区
// notification.timeZone = [NSTimeZone defaultTimeZone];
// 设置重复的间隔
// notification.repeatInterval = kCFCalendarUnitSecond;

// 附加操作
// notification.category = @"choose";

// 4.执行本地通知
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}

/*
App处于kill掉时,点击通知栏进入App,使用该方法接收本地(远程)通知
*/
- (void)receiveLocalNotificationWithLaunchOptions:(NSDictionary *)launchOptions{
/*
App被kill掉,通过通知启动App
1.本地的 key - UIApplicationLaunchOptionsLocalNotificationKey
2.远程的 key - UIApplicationLaunchOptionsRemoteNotificationKey
*/
if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]) {
UILocalNotification * notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
[self handleLocalNotificationWithWithNotification:notification userInfo:launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]];
}
}
// 处理本地通知
- (void)handleLocalNotificationWithWithNotification:(UILocalNotification *)notification userInfo:(NSDictionary *)userInfo{
NSLog(@"title:%@ body:%@",notification.alertTitle,notification.alertBody);
}

@end

  1. AppDelegate 中实现
1
2
3
4
// 本地通知授权
[[LocalNotiManager sharedManager] localNotificationAuthorization];
// 接收本地通知
[[LocalNotiManager sharedManager] receiveLocalNotificationWithLaunchOptions:launchOptions];
  1. AppDelegate 中回调
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#pragma mark - 接收本地通知
/*
App处于挂起或运行时,调用该方法接收本地(远程)通知
1.当App在前台状态下,点击推送通知,会调用该方法
2.当App在后台状态下,点击推送通知,会调用该方法(从锁屏界面点击推送通知也会执行)
*/
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
NSLog(@"%@",notification);
// 前台点击通知消息
if (application.applicationState == UIApplicationStateActive) {
NSLog(@"处理前台接受通知的业务");
}
// 后台点击通知消息
else {
NSLog(@"处理后台台接受通知的业务");
[[LocalNotiManager sharedManager] handleLocalNotificationWithWithNotification:notification userInfo:notification.userInfo];
}
}
// 当用户点击允许或者不允许时,会执行如下代理方法,我们在其中实现处理逻辑
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings{
NSLog(@"当用户点击允许或者不允许时,会执行如下代理方法");
}
  • Post title:OC学习68:本地通知UserNotifications Framework
  • Post author:张建
  • Create time:2023-08-10 16:35:12
  • Post link:https://redefine.ohevan.com/2023/08/10/OC/OC学习68:本地通知UserNotifications Framework/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.
On this page
OC学习68:本地通知UserNotifications Framework