iOS常用通知
1、本地推送通知:(Local Notification):
不需要联网就可以发出的通知, 常用于提醒用户完成一些任务,如:闹钟,日历待办事项提醒,备注等等;
2、远程推送通知:(Remote Notification)
从远程服务器(APNs)推送给客户端的通知(需要联网), 模拟器无法调试远程推送;
常用于解决获取传统数据的局限性,能让数据实时更新, 在联网的情况下, 苹果设备都会与APNs建立长连接, 因此能保证数据传输速度快及保证数据的实时性...
本篇只介绍本地推送通知:
通知的授权与注册: (分iOS10.0 以后 与 之前)
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 通知的授权检查
[self checkNotificationAuthorization];
// 本地通知 + App未存活 (iOS 10.0以前)
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (notification.userInfo) {
// 处理收到的通知
}
return YES;
}
/**
检查本地通知的授权
*/
- (void)checkNotificationAuthorization {
if (@available(iOS 10.0, *)) {
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
// 这两个必须要写在上面 每次程序重新打开时都得设置一遍...
center.delegate = self;
//添加category: iOS 10.0以后
[center setNotificationCategories:[self createNotificationCategoryActionsForNewVersion]];
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) { // 判断是否是未选择授权状态
// 若未授权, 则请求授权
[center requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
NSLog(@"授权开启成功");
} else {
NSLog(@"授权开启失败");
}
}];
}
}];
} else {
//添加category: iOS 8.0以后
UIUserNotificationCategory * category = [self adduserNotificationCategory];
UIUserNotificationSettings * setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:[NSSet setWithObject:category]];
[[UIApplication sharedApplication] registerUserNotificationSettings:setting];
}
}
添加 category (即在通知栏重压时设置其按钮)
//添加category: iOS 10.0以后
-(NSSet *)createNotificationCategoryActionsForNewVersion{
//注册本地通知用到的Action
//进入app按钮
UNNotificationAction * localAction = [UNNotificationAction actionWithIdentifier:kCategoryActionIdentifierOpen title:@"查看通知" options:UNNotificationActionOptionForeground];
//取消按钮
UNNotificationAction *localCancel = [UNNotificationAction actionWithIdentifier:kCategoryActionIdentifierCancel title:@"忽略" options:UNNotificationActionOptionDestructive];
//将这些action带入category
UNNotificationCategory *localCategory = [UNNotificationCategory categoryWithIdentifier:kUNCategoryIdentifier actions:@[localAction,localCancel] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];
return [NSSet setWithObjects:localCategory,nil];
}
//添加category: iOS 8.0以后
- (UIMutableUserNotificationCategory *)adduserNotificationCategory {
//UIMutableUserNotificationAction用来添加自定义按钮
UIMutableUserNotificationAction * responseAction = [[UIMutableUserNotificationAction alloc] init];
responseAction.identifier = kCategoryActionIdentifierOpen;
responseAction.title = @"查看通知";
responseAction.activationMode = UIUserNotificationActivationModeForeground; //点击的时候启动程序
responseAction.authenticationRequired = YES;//需要解锁权限
UIMutableUserNotificationAction *deleteAction = [[UIMutableUserNotificationAction alloc] init];
deleteAction.identifier = kCategoryActionIdentifierCancel;
deleteAction.title = @"忽略";
deleteAction.activationMode = UIUserNotificationActivationModeBackground; //点击的时候不启动程序,后台处理
deleteAction.destructive = YES; //YES为红色,NO为蓝色
UIMutableUserNotificationCategory *category = [[UIMutableUserNotificationCategory alloc] init];
category.identifier = kUNCategoryIdentifier;//用于将该 category 标识的同时,那一个 notification 实例需要使用这个 category 的 actions 也是传入这个值给 notification 的。
//UIUserNotificationActionContextDefault:默认添加可以添加两个自定义按钮
//UIUserNotificationActionContextMinimal:四个自定义按钮
[category setActions:@[responseAction, deleteAction] forContext:UIUserNotificationActionContextDefault];
return category;
}
添加通知
这里是点击一个按钮事件来添加通知的:
- (IBAction)addLocalNotification:(UIButton *)sender {
NSDictionary * dict = @{@"businessId": @"kLocalNotificationTest",@"title": @"本地通知测试",@"memo": @"通知发送成功", @"startTime": @"2019-10-28 10:10:10"};
NSDictionary * dict1 = @{@"businessId": @"kLocalNotificationTestOne",@"title": @"本地通知测试__One",@"memo": @"通知发送成功__One", @"startTime": @"2019-10-28 10:10:10"};
NSArray * arr = @[dict, dict1];
// 添加两个通知
for (NSDictionary * dataDic in arr) {
NSData * data = [NSJSONSerialization dataWithJSONObject:dataDic options:NSJSONWritingPrettyPrinted error:nil];
NSString * string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[self addLocalNotificationWithData:string];
}
}
/**
添加本地通知
@param dataString 通知的内容: 字典序列化后的字符串
*/
- (void)addLocalNotificationWithData: (NSString *)dataString {
NSData * data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
if (@available(iOS 10.0, *)) {
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (settings.authorizationStatus == UNAuthorizationStatusDenied) {
NSLog(@"用户未授权");
return;
}
UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc] init];
self.badgeNum++;
content.badge = @(self.badgeNum);
content.title = dict[@"title"];
content.body = dict[@"memo"];
content.userInfo = @{@"type" : kNotificationScheduleType, @"data": dict};;
// 这个必须要加 且要和 UNNotificationCategory 的Identifier 保持一致
content.categoryIdentifier = kUNCategoryIdentifier;
UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5.f repeats:NO];
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:dict[@"businessId"] content:content trigger:trigger];
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"添加本地通知报错: %@", error);
}
}];
}];
} else {
if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) {
NSLog(@"用户未授权");
return;
}
UILocalNotification * notification = [[UILocalNotification alloc] init];
notification.timeZone = [NSTimeZone defaultTimeZone];
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:5.f];
notification.alertTitle = dict[@"title"];
notification.alertBody = dict[@"memo"];
self.badgeNum++;
notification.applicationIconBadgeNumber = self.badgeNum;
notification.soundName = UILocalNotificationDefaultSoundName;
notification.alertAction = @"查看";
notification.userInfo = @{@"type" : kNotificationScheduleType, @"data": dict};;
notification.category = kUNCategoryIdentifier;
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
}
通知的回调处理:
iOS10.0 后是通知代理来回调的 UNUserNotificationCenterDelegate
// App在前台获取通知
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler API_AVAILABLE(ios(10.0)){
completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound);
}
// 点击通知进入App
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler API_AVAILABLE(ios(10.0)){
completionHandler();
NSDictionary * userInfo = response.notification.request.content.userInfo;
if(userInfo[@"type"] && [userInfo[@"type"] isEqualToString:kNotificationScheduleType]) {
if([response.actionIdentifier isEqualToString:kCategoryActionIdentifierCancel] || [response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier]) {
// 通知栏点击 忽略 或 清除 时的回调
} else {
// 点击 打开 或 查看 时的回调
}
}
}
iOS10.0 以前的通知回调:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// 本地通知 + App未存活
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (notification.userInfo) {
// 处理回调回来的通知事件
}
return YES;
}
// APP在前台运行中收到 本地通知 时调用, 以及App 处于后台挂起(suspended)状态,但未 terminated 时,点击通知启动都是这个方法进行响应
// 即 App 存活
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
}
//调用过用户注册通知方法之后执行(也就是调用完registerUserNotificationSettings:方法之后执行)
-(void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
}
// APP 将要进入前台时调用
- (void)applicationWillEnterForeground:(UIApplication *)application {
// 设置App图标通知数量显示
}
//iOS 8 ~ 9 ,当点击本地通知自定义的响应按钮(action btn)时,根据按钮的 activeMode 模式,回调以下方法
//1. ActivationModeForeground 的 action , 会启动 App 同时回调方法
//2. ActivationModeBackground 的 action 不启动 App 让 App 在 background 下回调方法
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler {
if ([identifier isEqualToString:@"kCategoryActionIdentifierOpen"]) {
//ActivationModeForeground 的 action , 启动 App 让 App 在 Foreground 下响应
} else {
//ActivationModeBackground 的 action 不启动 App 让 App 在 background 下响应
NSLog(@"%s -- %@ -- identifier %@ --- thread %@", __func__, notification, identifier, [NSThread currentThread]);
// //下面代码用于测试,退出 App 后接收到 本地通知时,点击后台action时是否执行了这个响应方法。实测执行了的
// [[NSUserDefaults standardUserDefaults] setObject:@"ActivationModeBackground 的 action 不启动 App 让 App 在 background 下响应" forKey:@"IGNOREKEY"];
// [[NSUserDefaults standardUserDefaults] synchronize];
}
completionHandler(); //根据Action btn 的 identifier 处理自定义事件后应该马上调用 completionHandler block,如果调用 completionHandler block 失败的话,App 会立即 terminated。
}
移除通知 或 移除指定通知
#pragma mark -- 移除本地通知
/**
移除指定的通知
@param identifierStr 通知的唯一标识 或 信息
*/
- (void)removeNotificationWithIdentifier:(NSString *)identifierStr {
//获取本地通知数组 (该数组会持有需要重复 fired 的 已被 copy 的 notification 对象,用于到达下次间隔时再 fire, 如果不需要重复的 notification,即 notification.repeatInterval = 0 的话,该 notification fire 之后不会被 copy 保留到这个数组里)
//本地通知最多只能有64个,超过会被系统忽略
if (@available(iOS 10.0, *)) {
[[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[identifierStr]];
} else {
NSArray *notifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
for (UILocalNotification * notification in notifications) {
NSString * businessId = notification.userInfo[@"businessId"];
if ([identifierStr isEqualToString:businessId]) {
[[UIApplication sharedApplication] cancelLocalNotification:notification];
}
}
}
/*
执行取消本地通知的场景:
1. 已经响应过的本地通知,需要取消。
2. 已经递交到 application 的,但在 fire 之前 确定要取消的通知,需要取消。如提醒任务的取消,或更改提醒时间(此时应该是新的一个本地通知了)
*/
}
/**
移除所有的通知
*/
- (void)removeAllNotification {
if (@available(iOS 10.0, *)) {
[[UNUserNotificationCenter currentNotificationCenter] removeAllPendingNotificationRequests];
} else {
[[UIApplication sharedApplication] cancelAllLocalNotifications];
}
}
因代码是在项目里抽取的, 有不正之处欢迎指证...
***全部代码:
AppDelegate.h
#import "AppDelegate.h"
#import "LocalNotification/LocalNotificationManager.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[[LocalNotificationManager shareLocalNotification] checkNotificationAuthorization];
[[LocalNotificationManager shareLocalNotification] removeAllNotification];
// 本地通知 + App未存活
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (notification.userInfo) {
[[NSUserDefaults standardUserDefaults] setObject:notification.userInfo forKey:@"localNotificationKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
return YES;
}
// APP在前台运行中收到 本地通知 时调用, 以及App 处于后台挂起(suspended)状态,但未 terminated 时,点击通知启动都是这个方法进行响应
// 即 App 存活
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
[[LocalNotificationManager shareLocalNotification] receiveLocalNotificationWithUserInfo:notification.userInfo];
}
//调用过用户注册通知方法之后执行(也就是调用完registerUserNotificationSettings:方法之后执行)
-(void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
NSLog(@"%s",__func__);
}
// APP 将要进入前台时调用
- (void)applicationWillEnterForeground:(UIApplication *)application {
// 设置这个后, 后导致通知栏里的通知, 点击其中任何一个后, 其它通知都会消失...
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
[LocalNotificationManager shareLocalNotification].badgeNum = 0;
}
//iOS 8 ~ 9 ,当点击本地通知自定义的响应按钮(action btn)时,根据按钮的 activeMode 模式,回调以下方法
//1. ActivationModeForeground 的 action , 会启动 App 同时回调方法
//2. ActivationModeBackground 的 action 不启动 App 让 App 在 background 下回调方法
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler {
if ([identifier isEqualToString:@"kCategoryActionIdentifierOpen"]) {
//ActivationModeForeground 的 action , 启动 App 让 App 在 Foreground 下响应
[[LocalNotificationManager shareLocalNotification] receiveLocalNotificationWithUserInfo:notification.userInfo];
} else {
NSInteger badgeNumber = [LocalNotificationManager shareLocalNotification].badgeNum;
badgeNumber = badgeNumber > 0 ? --badgeNumber : 0;
[LocalNotificationManager shareLocalNotification].badgeNum = badgeNumber;
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:badgeNumber];
//ActivationModeBackground 的 action 不启动 App 让 App 在 background 下响应
NSLog(@"%s -- %@ -- identifier %@ --- thread %@", __func__, notification, identifier, [NSThread currentThread]);
// //下面代码用于测试,退出 App 后接收到 本地通知时,点击后台action时是否执行了这个响应方法。实测执行了的
// [[NSUserDefaults standardUserDefaults] setObject:@"ActivationModeBackground 的 action 不启动 App 让 App 在 background 下响应" forKey:@"IGNOREKEY"];
// [[NSUserDefaults standardUserDefaults] synchronize];
}
completionHandler(); //根据Action btn 的 identifier 处理自定义事件后应该马上调用 completionHandler block,如果调用 completionHandler block 失败的话,App 会立即 terminated。
}
@end
// 抽取的通知管理单例
LocalNotificationManager.h
#import
NS_ASSUME_NONNULL_BEGIN
typedef void(^LocalNotificationManagerBlock)(NSString * userInfoString);
@interface LocalNotificationManager : NSObject
// 消息的数量
@property (nonatomic, assign, readwrite) NSInteger badgeNum;
@property (nonatomic, copy) LocalNotificationManagerBlock managerBlock;
+ (instancetype)shareLocalNotification;
/**
检查本地通知的授权
*/
- (void)checkNotificationAuthorization;
/**
添加本地通知
@param dataString 通知的内容: 字典序列化后的字符串
*/
- (void)addLocalNotificationWithData: (NSString *)dataString;
/**
移除指定的通知
@param identifierStr 通知的唯一标识 或 信息
*/
- (void)removeNotificationWithIdentifier: (NSString *)identifierStr;
/**
移除所有的通知
*/
- (void)removeAllNotification;
/**
收到通知后的回调
@param block block回调
*/
- (void)handleLocalNotificationWithBlock: (LocalNotificationManagerBlock)block;
/**
收到通知后的数据处理
@param userInfo 通知携带的数据
*/
- (void)receiveLocalNotificationWithUserInfo: (NSDictionary *)userInfo;
@end
NS_ASSUME_NONNULL_END
LocalNotificationManager.m
#import "LocalNotificationManager.h"
#import
#import
static NSString * const kNotificationScheduleType = @"kNotificationScheduleType";
static NSString * const kUNCategoryIdentifier = @"kUNCategoryIdentifier";
static NSString * const kCategoryActionIdentifierOpen = @"kCategoryActionIdentifierOpen";
static NSString * const kCategoryActionIdentifierCancel = @"kCategoryActionIdentifierCancel";
@interface LocalNotificationManager()
@end
@implementation LocalNotificationManager
+ (instancetype)shareLocalNotification {
static LocalNotificationManager * _instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[LocalNotificationManager alloc] init];
});
return _instance;
}
- (void)checkNotificationAuthorization {
if (@available(iOS 10.0, *)) {
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
// 这两个必须要写在上面 每次程序重新打开时都得设置一遍...
center.delegate = self;
[center setNotificationCategories:[self createNotificationCategoryActionsForNewVersion]];
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) { // 判断是否是未选择授权状态
[center requestAuthorizationWithOptions:UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert completionHandler:^(BOOL granted, NSError * _Nullable error) {
}];
}
}];
} else {
UIUserNotificationCategory * category = [self adduserNotificationCategory];
UIUserNotificationSettings * setting = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert categories:[NSSet setWithObject:category]];
[[UIApplication sharedApplication] registerUserNotificationSettings:setting];
}
}
- (void)addLocalNotificationWithData: (NSString *)dataString {
NSData * data = [dataString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary * dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSTimeInterval noticInterval = [self diffNowDateWithNotificationTime:dict[@"startTime"]];
if (noticInterval <= 0) {
return;
}
if (@available(iOS 10.0, *)) {
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
if (settings.authorizationStatus == UNAuthorizationStatusDenied) {
NSLog(@"用户未授权");
return;
}
UNMutableNotificationContent * content = [[UNMutableNotificationContent alloc] init];
self.badgeNum++;
content.badge = @(self.badgeNum);
content.title = dict[@"title"];
content.body = dict[@"memo"];
content.userInfo = @{@"type" : kNotificationScheduleType, @"data": dict};;
// 这个必须要加 且要和 UNNotificationCategory 的Identifier 保持一致
content.categoryIdentifier = kUNCategoryIdentifier;
UNTimeIntervalNotificationTrigger * trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:5.f repeats:NO];
UNNotificationRequest * request = [UNNotificationRequest requestWithIdentifier:dict[@"businessId"] content:content trigger:trigger];
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"添加本地通知报错: %@", error);
}
}];
}];
} else {
if ([[UIApplication sharedApplication] currentUserNotificationSettings].types == UIUserNotificationTypeNone) {
NSLog(@"用户未授权");
return;
}
UILocalNotification * notification = [[UILocalNotification alloc] init];
notification.timeZone = [NSTimeZone defaultTimeZone];
notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:5.f];
notification.alertTitle = dict[@"title"];
notification.alertBody = dict[@"memo"];
self.badgeNum++;
notification.applicationIconBadgeNumber = self.badgeNum;
notification.soundName = UILocalNotificationDefaultSoundName;
notification.alertAction = @"查看";
notification.userInfo = @{@"type" : kNotificationScheduleType, @"data": dict};;
notification.category = kUNCategoryIdentifier; // 与下面创建的category 的identifier必须保持一致
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
}
//添加category: iOS 10.0以后
-(NSSet *)createNotificationCategoryActionsForNewVersion{
//注册本地通知用到的Action
//进入app按钮
UNNotificationAction * localAction = [UNNotificationAction actionWithIdentifier:kCategoryActionIdentifierOpen title:@"查看通知" options:UNNotificationActionOptionForeground];
//取消按钮
UNNotificationAction *localCancel = [UNNotificationAction actionWithIdentifier:kCategoryActionIdentifierCancel title:@"忽略" options:UNNotificationActionOptionDestructive];
//将这些action带入category
UNNotificationCategory *localCategory = [UNNotificationCategory categoryWithIdentifier:kUNCategoryIdentifier actions:@[localAction,localCancel] intentIdentifiers:@[] options:UNNotificationCategoryOptionCustomDismissAction];
return [NSSet setWithObjects:localCategory,nil];
}
//添加category: iOS 8.0以后
- (UIMutableUserNotificationCategory *)adduserNotificationCategory {
//UIMutableUserNotificationAction用来添加自定义按钮
UIMutableUserNotificationAction * responseAction = [[UIMutableUserNotificationAction alloc] init];
responseAction.identifier = kCategoryActionIdentifierOpen;
responseAction.title = @"查看通知";
responseAction.activationMode = UIUserNotificationActivationModeForeground; //点击的时候启动程序
responseAction.authenticationRequired = YES;//需要解锁权限
UIMutableUserNotificationAction *deleteAction = [[UIMutableUserNotificationAction alloc] init];
deleteAction.identifier = kCategoryActionIdentifierCancel;
deleteAction.title = @"忽略";
deleteAction.activationMode = UIUserNotificationActivationModeBackground; //点击的时候不启动程序,后台处理
deleteAction.destructive = YES; //YES为红色,NO为蓝色
UIMutableUserNotificationCategory *category = [[UIMutableUserNotificationCategory alloc] init];
category.identifier = kUNCategoryIdentifier;//用于将该 category 标识的同时,那一个 notification 实例需要使用这个 category 的 actions 也是传入这个值给 notification 的。
//UIUserNotificationActionContextDefault:默认添加可以添加两个自定义按钮
//UIUserNotificationActionContextMinimal:四个自定义按钮
[category setActions:@[responseAction, deleteAction] forContext:UIUserNotificationActionContextDefault];
return category;
}
#pragma mark -- UNUserNotificationCenterDelegate
// App在前台获取通知
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler API_AVAILABLE(ios(10.0)){
completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound);
}
// 点击通知进入App
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler API_AVAILABLE(ios(10.0)){
completionHandler();
NSDictionary * userInfo = response.notification.request.content.userInfo;
if(userInfo[@"type"] && [userInfo[@"type"] isEqualToString:kNotificationScheduleType]) {
if([response.actionIdentifier isEqualToString:kCategoryActionIdentifierCancel] || [response.actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier]) {
self.badgeNum = self.badgeNum > 0 ? --self.badgeNum : 0;
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:self.badgeNum];
} else {
[self receiveLocalNotificationWithUserInfo:userInfo[@"data"]];
}
}
}
#pragma mark -- 移除本地通知
- (void)removeNotificationWithIdentifier:(NSString *)identifierStr {
//获取本地通知数组 (该数组会持有需要重复 fired 的 已被 copy 的 notification 对象,用于到达下次间隔时再 fire, 如果不需要重复的 notification,即 notification.repeatInterval = 0 的话,该 notification fire 之后不会被 copy 保留到这个数组里)
//本地通知最多只能有64个,超过会被系统忽略
if (@available(iOS 10.0, *)) {
[[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:@[identifierStr]];
} else {
NSArray *notifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
for (UILocalNotification * notification in notifications) {
NSString * businessId = notification.userInfo[@"businessId"];
if ([identifierStr isEqualToString:businessId]) {
[[UIApplication sharedApplication] cancelLocalNotification:notification];
}
}
}
//删除指定通知
// [[UIApplication sharedApplication] cancelLocalNotification:notifications[0]];
//删除所有通知
// [[UIApplication sharedApplication] cancelAllLocalNotifications];
/*
执行取消本地通知的场景:
1. 已经响应过的本地通知,需要取消。
2. 已经递交到 application 的,但在 fire 之前 确定要取消的通知,需要取消。如提醒任务的取消,或更改提醒时间(此时应该是新的一个本地通知了)
*/
}
- (void)removeAllNotification {
self.badgeNum = 0;
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
if (@available(iOS 10.0, *)) {
[[UNUserNotificationCenter currentNotificationCenter] removeAllPendingNotificationRequests];
} else {
[[UIApplication sharedApplication] cancelAllLocalNotifications];
}
}
#pragma mark -- 处理收到的通知
- (void)handleLocalNotificationWithBlock:(LocalNotificationManagerBlock)block {
NSDictionary * dict = [[NSUserDefaults standardUserDefaults] objectForKey:@"localNotificationKey"];
if (dict) {
[self receiveLocalNotificationWithUserInfo:dict];
}
self.managerBlock = block;
}
- (void)receiveLocalNotificationWithUserInfo:(NSDictionary *)userInfo {
if (!userInfo) {
[self showInfo:@"消息失败"];
return;
}
self.badgeNum = self.badgeNum > 0 ? --self.badgeNum : 0;
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:self.badgeNum];
NSData * data = [NSJSONSerialization dataWithJSONObject:userInfo options:NSJSONWritingPrettyPrinted error:nil];
NSString * string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[self showInfo:string];
}
// 通知消息的显示
- (void)showInfo:(NSString *)infoStr {
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"" message:infoStr preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:NULL]];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];
}
// 获取当前时间与指定时间之间的时间戳
- (CGFloat)diffNowDateWithNotificationTime: (NSString *)timeStr {
NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:@"Asia/Beijing"];
[formatter setTimeZone:timeZone];
NSDate * noticDate = [formatter dateFromString:timeStr];
NSTimeInterval noticInterval = [noticDate timeIntervalSince1970] * 1;
NSTimeInterval nowInterval = [[NSDate date] timeIntervalSince1970] * 1;
return noticInterval - nowInterval;
}
@end
添加通加的数据格式:
- (IBAction)addLocalNotification:(UIButton *)sender {
NSDictionary * dict = @{@"businessId": @"kLocalNotificationTest",@"title": @"本地通知测试",@"memo": @"通知发送成功", @"startTime": @"2019-10-28 10:10:10"};
NSDictionary * dict1 = @{@"businessId": @"kLocalNotificationTestOne",@"title": @"本地通知测试__One",@"memo": @"通知发送成功__One", @"startTime": @"2019-10-28 10:10:10"};
NSArray * arr = @[dict, dict1];
for (NSDictionary * dataDic in arr) {
NSData * data = [NSJSONSerialization dataWithJSONObject:dataDic options:NSJSONWritingPrettyPrinted error:nil];
NSString * string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[[LocalNotificationManager shareLocalNotification] addLocalNotificationWithData:string];
}
}