实现快捷创建简单的定时重复提醒推送功能。
主要实现原理
iOS10及以上
1.获取通知权限
iOS10及以上要先请求通知权限
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
//请求获取通知权限(角标,声音,弹框)
[center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge |
UNAuthorizationOptionSound |
UNAuthorizationOptionAlert)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
//获取用户是否同意开启通知
NSLog(@"开启通知成功!");
}
}];
2.创建通知
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = attribute[ZBNotificationAlertTitle];
// content.subtitle = @"本地通知副标题";
content.body = @"body";
//角标数量
content.badge = @1;
content.userInfo = userInfo;
//设置通知声音
UNNotificationSound *sound = [UNNotificationSound defaultSound];
content.sound = sound;
创建 UNNotificationRequest :
//设置时间容器:传人date中所有时间元素放入时间容器
NSDateComponents * components = [[NSCalendar currentCalendar]
components:NSCalendarUnitYear |
NSCalendarUnitMonth |
NSCalendarUnitWeekday |
NSCalendarUnitDay |
NSCalendarUnitHour |
NSCalendarUnitMinute |
NSCalendarUnitSecond
fromDate:date];
//设置UNCalendarNotificationTrigger
//repeats: 设置是否重复
UNCalendarNotificationTrigger * trigger = [UNCalendarNotificationTrigger
triggerWithDateMatchingComponents:components
repeats:repeat];
//设置UNNotificationRequest
//identifer:设置通知标识符或者说通知名字
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifer content:content trigger:trigger];
3.添加通知
//把通知加到UNUserNotificationCenter, 到指定触发点会被触发
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error) {
NSLog(@"通知添加失败:%@",error);
} else {
NSLog(@"通知添加成功");
}
}];
4.重复提醒
如果repeats为YES时为重复提醒
约定在特定时间提醒时我们用时间容器来实现
NSDateComponents * components = [[NSCalendar currentCalendar]
components:NSCalendarUnitYear |
NSCalendarUnitMonth |
NSCalendarUnitWeekday |
NSCalendarUnitDay |
NSCalendarUnitHour |
NSCalendarUnitMinute |
NSCalendarUnitSecond
fromDate:date];
比如设置一个每天都提醒的推送,传入的时间设置为:
NSDateFormatter * formatter = [[NSDateFormatter alloc]init];
[formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
NSDate * date = [formatter dateFromString:@"2019-01-01 08:00:00"];
则 每天8点 进行推送提醒的 时间容器 为:
NSDateComponents * components = [[NSCalendar currentCalendar]
components:NSCalendarUnitHour |
NSCalendarUnitMinute |
NSCalendarUnitSecond
fromDate:date];
每周二8点 (19年1月1号为周二) 进行推送提醒的 时间容器 为:
NSDateComponents * components = [[NSCalendar currentCalendar]
components:NSCalendarUnitWeekday |
NSCalendarUnitHour |
NSCalendarUnitMinute |
NSCalendarUnitSecond
fromDate:date];
每月1号8点 进行推送提醒的 时间容器 为:
NSDateComponents * components = [[NSCalendar currentCalendar]
components:NSCalendarUnitDay |
NSCalendarUnitHour |
NSCalendarUnitMinute |
NSCalendarUnitSecond
fromDate:date];
每年1月1号8点 进行推送提醒的 时间容器 为:
NSDateComponents * components = [[NSCalendar currentCalendar]
components:NSCalendarUnitMonth |
NSCalendarUnitDay |
NSCalendarUnitHour |
NSCalendarUnitMinute |
NSCalendarUnitSecond
fromDate:date];
以此类推..
如果有需求是要 周一到周五的每天8点 提醒时,我的做法是用for循环创建5个通知:
//因为周日是一星期第一天,1代表周日,所以周一从2开始
for (NSInteger i = 2; i <= 6; i++) {
//这里时间容器创建和以上每周提醒的一样,省略。
components.weekday = i;
//然后用这个components去添加通知就可以实现重复通知了
}
5.取消通知
//找到要取消的通知名字
NSMutableArray * names = [[NSMutableArray alloc]initWithObjects:notificationName, nil];
//批量取消这些通知
[[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:names];
iOS10以下
1.创建通知
UILocalNotification *localNotification = [[UILocalNotification alloc]init];
// 设置触发时间
localNotification.fireDate = date;
// 设置时区 以当前手机运行的时区为准
localNotification.timeZone = [NSTimeZone localTimeZone];
// 设置推送 显示的内容
localNotification.alertTitle = @"title";
localNotification.alertBody = @"body";
// 设置 角标
localNotification.applicationIconBadgeNumber = 1;
// 不设置此属性,则默认不重复
localNotification.repeatInterval = repeatInterval;
// 设置推送的声音
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.userInfo = userInfo;
//添加到通知
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
2.重复提醒
重复提醒通过设置 repeatInterval 就可以
value | effect |
---|---|
NSCalendarUnitDay | 每天 |
NSCalendarUnitWeekday | 每周 |
NSCalendarUnitMonth | 每月 |
NSCalendarUnitYear | 每年 |
... | ... |
等等...
同样如果有需求是周一到周五某时提醒也用循环创建五个通知
for (NSInteger i = 2; i <= 6; i++) {
//同样用NSDateComponents设置weekday来实现星期几的提醒
//先用传入的date创建时间容器,然后修改weekday后再转为NSDate
NSDateComponents * components = [[NSCalendar currentCalendar] components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:date];
components.weekday = i;
NSDate * newDate = [[NSCalendar currentCalendar] dateFromComponents:components];
//吧newDate再赋值给fireDate
}
3.取消通知
//获取所有通知
NSArray * localNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications];
for (UILocalNotification * notification in localNotifications) {
//找到和要删除的通知同名的通知删除
if ([notification.userInfo[@"notificationName"] hasPrefix:notificationName]) {
//删除通知
[[UIApplication sharedApplication] cancelLocalNotification:notification];
}
}
注册通知
//注册通知
[[UIApplication sharedApplication] registerForRemoteNotifications];
快捷使用
封装了一个可以直接使用的类:ZBLocalNotification
//提醒重复类型
typedef NS_ENUM(NSInteger, ZBLocalNotificationRepeat) {
ZBLocalNotificationRepeatNone, //不重复
ZBLocalNotificationRepeatEveryDay, //每天
ZBLocalNotificationRepeatEveryWeek, //每周
ZBLocalNotificationRepeatEveryMonth, //每月
ZBLocalNotificationRepeatEveryYear, //每年
ZBLocalNotificationRepeatEveryWorkDay //每周一到周五(工作日)
};
//标识通知属性的key
typedef NSString * ZBLocalNotificationKey;
//标识通知声音文件名字的key
typedef NSString * ZBLocalNotificationSoundName;
extern ZBLocalNotificationKey const ZBNotificationFireDate; //标识提醒时间
extern ZBLocalNotificationKey const ZBNotificationAlertTitle; //标识标题
extern ZBLocalNotificationKey const ZBNotificationAlertBody; //标识提醒内容
extern ZBLocalNotificationKey const ZBNotificationAlertAction; //标识按钮
extern ZBLocalNotificationKey const ZBNotificationSoundName; //标识声音
extern ZBLocalNotificationKey const ZBNotificationUserInfoName; //标识通知名字
extern ZBLocalNotificationKey const ZBNotificationPriority; //标识通知优先级
extern ZBLocalNotificationKey const ZBNotificationRepeat; //标识通知重复
extern ZBLocalNotificationSoundName const ZBNotificationSoundAlarm; //标识声音为提醒
extern ZBLocalNotificationSoundName const ZBNotificationSoundOther; //标识声音为其他
@interface ZBLocalNotification : NSObject
/**
创建本地通知
@param attribute 通知的属性
*/
+ (void)createLocalNotificationWithAttribute:(NSDictionary *)attribute;
/**
取消通知
@param notificationName 通知名字
*/
+ (void)cancelLocalNotificationWithName:(NSString *)notificationName;
#ifdef NSFoundationVersionNumber_iOS_9_x_Max
/**
注册通知
*/
+ (void)requestUNUserNotificationAuthorization;
#endif
@end
创建时间为date的每天重复提醒例子:
导入#import "ZBLocalNotification.h"
[ZBLocalNotification createLocalNotificationWithAttribute:
@{ZBNotificationUserInfoName:@"notificationName",
ZBNotificationSoundName:ZBNotificationSoundAlarm,
ZBNotificationAlertBody:@"提醒内容",
ZBNotificationAlertTitle:@"提醒标题",
ZBNotificationFireDate:date,
ZBNotificationPriority:@(0),
ZBNotificationRepeat:@(ZBLocalNotificationRepeatEveryDay)
}];
取消名字为 “notificationName” 的通知:
[ZBLocalNotification cancelLocalNotificationWithName:@"notificationName"];
通知优先级问题
如果设置了多个推送通知,并且时间都在同一个时刻时,就是同时收到多个推送通知时,想要只显示优先级最高的一个,我是在AppDelegate里这样处理的(希望有更好的方法能不吝赐教 :) ):
先创建一个userInfo容器,保存同一时间收到的通知
self.userInfos = [[NSMutableArray alloc]init];
//处理接收到的通知信息
- (void)filteredUserInfo {
if (self.userInfos.count == 0) {
return;
}
//选出你希望显示的通知信息,以下方法是显示优先级高的,你可以判断不同的条件
//排序所有收到的通知信息,对比优先级,把优先级最高的放首位
[self.userInfos sortUsingComparator:^NSComparisonResult(NSDictionary * obj1, NSDictionary * obj2) {
if ([obj1[ZBNotificationPriority] integerValue] < [obj2[ZBNotificationPriority] integerValue]) {
return NSOrderedDescending;
}
return NSOrderedSame;
}];
//自定义展示推送内容
[self showAlarmAlertWithUserInfo:self.userInfos.firstObject];
//重置userInfo容器
[self.userInfos removeAllObjects];
}
//添加通知到userInfo容器
- (void)waitMultipleUserInfo:(NSDictionary *)userInfo {
[self.userInfos addObject:userInfo];
//创建信号量,设置为0
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(quene, ^{
if (self.userInfos.count == 1) {
//信号量为0时,那么这个函数就阻塞当前线程等待timeout,时间到后继续执行
//0.3秒内第一次进入则等待0.3秒,0.3秒后对本时间段内提醒提取优先级最高的一个
//就是保存在极短时间内(我这里设置为0.3s)收到的所有通知,然后进行处理
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC));
dispatch_async(dispatch_get_main_queue(), ^{
[self filteredUserInfo];
});
}
});
}
#pragma mark - localNotification
// iOS10以下 在前台收到推送回调
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification{
[self waitMultipleUserInfo:notification.userInfo];
}
#ifdef NSFoundationVersionNumber_iOS_9_x_Max
#pragma mark - UNUserNotificationCenterDelegate
// iOS10 在前台收到推送回调
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(nonnull UNNotification *)notification withCompletionHandler:(nonnull void (^)(UNNotificationPresentationOptions))completionHandler{
UNNotificationRequest *request = notification.request; // 收到推送的请求
UNNotificationContent *content = request.content; //收到推送消息的全部内容
if ([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) {
NSLog(@"iOS10 收到远程通知");
}else{
NSLog(@"ios10 收到本地通知userInfo:%@",content.userInfo);
[self waitMultipleUserInfo:content.userInfo];
}
completionHandler(UNNotificationPresentationOptionBadge |
UNNotificationPresentationOptionSound
);
}
#endif
代码下载
ZBLocalNotification类下载地址:
iOS本地推送封装(定时推送、重复提醒)
ZBLocalNotification类下载地址:
iOS本地推送封装(定时推送、重复提醒)
疑问
比如需求是可以自定义重复的时间:
- 每隔两周的周二早上8点提醒
- 每隔三个月的7号早上8点提醒
这种时候的怎么实现,NSDateComponents需要怎么设置?
谢谢!
欢迎讨论,未完待续