仿 iOS 系统闹钟
- 添加闹钟效果图
- 收到通知效果图
更新日志
2018.09.12 由于 iOS 系统限制了注册本地推送的数量,最大的注册量为 64 条,且一旦超出 64 条,所有的推送都将失效,故而在添加推送的时候做了一个判断,超过 64 条后,将不添加,以免影响已经添加的推送。
前言
最近项目中涉及到了本地通知的功能,索性就模仿系统闹钟写了个 demo,对于 iOS 系统闹钟,应该都比较熟悉,该 demo,基本实现了系统闹钟的全部功能。该 demo 本地通知使用的是 iOS10 推出的 UserNotifications, 关于 UserNotifications 的介绍和使用,网上已有诸多文章,在此就不多做赘述。
UNNotificationsManager
关于闹钟所使用到的 UserNotifications 库 做了一个简单的封装, 包含了注册通知,添加通知,以及 一些通知组件的 实现方法,同时提供了可供 外部使用的收到推送的通知
extern NSString * const UNDidReciveRemoteNotifationKey;//收到远程通知时调用
extern NSString * const UNDidReciveLocalNotifationKey; //收到本地通知时
extern NSString * const UNNotifationInfoIdentiferKey; //本地通知userinfo 里 Identifer的key值
一些其他方法,以 demo 为准
//注册本地通知+ (void)registerLocalNotification;#pragma mark -- AddNotification/* 添加通知* identifer 标识符* body 主体* title 标题* subTitle 子标题* weekDay 周几* date 日期* repeat 是否重复* music 音乐*/+ (void)addNotificationWithBody:(NSString *)bodytitle:(NSString *)titlesubTitle:(NSString *)subTitleweekDay:(NSInteger)weekDaydate:(NSDate *)datemusic:(NSString *)musicidentifer:(NSString *)identiferisRepeat:(BOOL)repeatcompletionHanler:(void (^)(NSError *))handler;#pragma mark -- NotificationManage/** identifer 标识符* 根据标识符 移除 本地通知*/+ (void)removeNotificationWithIdentifer:(NSString *)identifer;#pragma mark -- NSDateComponents/** return 日期组件 时分秒* ex 每天重复*/+ (NSDateComponents *)componentsEveryDayWithDate:(NSDate *)date;#pragma mark -- UNNotificationContent/* UNMutableNotificationContent 通知内容* title 标题* subTitle 子标题* body 主体*/+ (UNMutableNotificationContent *)contentWithTitle:(NSString *)titlesubTitle:(NSString *)subTitlebody:(NSString *)body;#pragma mark -- UNNotificationTrigger/* UNNotificationTrigger 通知触发器* interval 通知间隔* repeats 是否重复*/+ (UNNotificationTrigger *)triggerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats;
添加闹钟
普通闹钟
[UNNotificationsManager addNotificationWithContent:[UNNotificationsManager contentWithTitle:@"时钟" subTitle:nil body:nil sound:[UNNotificationSound soundNamed:self.music]] dateComponents:[UNNotificationsManager componentsWithDate:self.date] identifer:self.identifer isRepeat:self.repeats completionHanler:^(NSError *error) {NSLog(@"add error %@", error);}];
每天重复
[UNNotificationsManager addNotificationWithContent:[UNNotificationsManager contentWithTitle:@"时钟" subTitle:nil body:nil sound:[UNNotificationSound soundNamed:self.music]] dateComponents:[UNNotificationsManager componentsEveryDayWithDate:self.date] identifer:self.identifer isRepeat:self.repeats completionHanler:^(NSError *error) {NSLog(@"add error %@", error);
}];
每周重复(周一,周二等)
[self.repeatStrs enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {NSInteger week = 0;if ([obj containsString:@"周日"]) {week = 1;}else if([obj containsString:@"周一"]){week = 2;}else if([obj containsString:@"周二"]){week = 3;}else if([obj containsString:@"周三"]){week = 4;}else if([obj containsString:@"周四"]){week = 5;}else if([obj containsString:@"周五"]){week = 6;}else if([obj containsString:@"周六"]){week = 7;}[UNNotificationsManager addNotificationWithContent:[UNNotificationsManager contentWithTitle:@"闹钟" subTitle:nil body:nil sound:[UNNotificationSound soundNamed:self.music]] weekDay:week date:self.date identifer:self.identifer isRepeat:YES completionHanler:^(NSError *error) {NSLog(@"add error %@", error);}];}];
}
铃声
这里无法获取系统铃声和震动类型,自己在网上找了点铃声素材。 系统铃声需要 caf 格式,MP3 和 caf 格式相互转化方法如下
//控制台输入afconvert xxx.mp3 xxx.caf -d ima4 -f caff -v
通知栏选项
首先注册通知的时候需要 UNNotificationCategory 以及 UNNotificationAction
UNNotificationAction *action1 = [UNNotificationAction actionWithIdentifier:actionFiveMin title:@"5分钟后" options:UNNotificationActionOptionNone];UNNotificationAction *action2 = [UNNotificationAction actionWithIdentifier:actionHalfAnHour title:@"半小时后" options:UNNotificationActionOptionNone];UNNotificationAction *action3 = [UNNotificationAction actionWithIdentifier:actionOneHour title:@"1小时后" options:UNNotificationActionOptionNone];UNNotificationAction *action4 = [UNNotificationAction actionWithIdentifier:actionStop title:@"停止" options:UNNotificationActionOptionNone];UNNotificationCategory *category = [UNNotificationCategory categoryWithIdentifier:identiferStr actions:@[action1, action2,action3, action4] intentIdentifiers:@[] options:UNNotificationCategoryOptionNone];UNNotificationCategory *stopCategory = [UNNotificationCategory categoryWithIdentifier:categryStopIdf actions:@[action4] intentIdentifiers:@[] options:UNNotificationCategoryOptionNone];[center setNotificationCategories:[NSSet setWithArray:@[category,stopCategory]]];
然后在设置 UNMutableNotificationContent 的时候需要设置对应的 categoryIdentifier 这里区分了是否设置了稍候提醒
+ (void)addNotificationWithContent:(UNNotificationContent *)content identifer:(NSString *)identifer trigger:(UNNotificationTrigger *)trigger completionHanler:(void (^)(NSError *))handler {//设置 categoryUNMutableNotificationContent *aContent = [content mutableCopy];if ([identifer hasPrefix:@"isLater"]) {aContent.categoryIdentifier = categryLaterIdf;}else {aContent.categoryIdentifier = categryStopIdf;}[self addNotificationWithRequest:[UNNotificationRequest requestWithIdentifier:identifer content:aContent trigger:trigger] completionHanler:handler];
}
最后在用户点击导航栏控件的时候,根据 identifier 处理相应事件
//与导航控件交互的时候会调用
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler {NSLog(@"%s", __func__);[self handCommnet:response];completionHandler();
}
-(void)handCommnet:(UNNotificationResponse *)response
{NSString *actionIdef = response.actionIdentifier;NSDate *date;if ([actionIdef isEqualToString:actionStop]) {return;}else if ([actionIdef isEqualToString:actionFiveMin]) {date = [NSDate dateWithTimeIntervalSinceNow:5 * 60];}else if ([actionIdef isEqualToString:actionHalfAnHour]) {date = [NSDate dateWithTimeIntervalSinceNow:30 * 60];}else if ([actionIdef isEqualToString:actionOneHour]) {date = [NSDate dateWithTimeIntervalSinceNow:60 * 60];}if (date) {[UNNotificationsManager addNotificationWithContent:response.notification.request.content identifer:response.notification.request.identifier trigger:[UNNotificationsManager triggerWithDateComponents:[UNNotificationsManager componentsWithDate:date] repeats:NO] completionHanler:^(NSError *error) {NSLog(@"delay11111 %@", error);}];}
}
持续推送
本地铃声 时长小于 30s。当手机处于后台,息屏的时候,铃声音乐是可以放完的的,手机处于活跃状态,只会持续到推送消失,想要在活跃状态持续推送本地闹钟,需要用户在 设置-通知-横幅风格 选择持续。