基于Swift实现仿IOS闹钟

仿 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 *)body
                        title:(NSString *)title
                     subTitle:(NSString *)subTitle
                      weekDay:(NSInteger)weekDay
                         date:(NSDate *)date
                        music:(NSString *)music
                    identifer:(NSString *)identifer
                     isRepeat:(BOOL)repeat
             completionHanler:(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 *)title
                                 subTitle:(NSString *)subTitle
                                     body:(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 {

    //设置 category
    UNMutableNotificationContent *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。当手机处于后台,息屏的时候,铃声音乐是可以放完的的,手机处于活跃状态,只会持续到推送消失,想要在活跃状态持续推送本地闹钟,需要用户在 设置-通知-横幅风格 选择持续。

相关推荐
黑化旺仔9 小时前
【OC】KVO
macos·ios·objective-c·cocoa
HouWan11 小时前
SwiftUI 小技巧:如何读取 SwiftUI Font 的详细信息
ios·swiftui·apple
李游Leo11 小时前
HarmonyOS 7 实战开发 05:把页面做到可上线状态
ios·harmonyos
终端安全笔记16 小时前
描述文件、企业证书、MDM 不是三个名字,是三层
android·ios·智能手机
传奇开心果编程17 小时前
【SwiftUI入门练中学】第1课 从零开始
学习·ui·ios·swiftui·swift
丿Wayne1 天前
iOS Token 刷新实战(Actor 避免并发重复刷新)
ios·swift·token·actor·swift并发
终端安全笔记2 天前
iOS 27 给了租赁一个新工具,但它只认受监督的设备
android·网络·安全·ios
大熊猫侯佩2 天前
独立 App 首发完成 iPhone Duo 展开态适配
ios·swiftui·swift
光影少年2 天前
setImmediate 和 setTimeout(0) 的区别
android·前端·react.js·ios·前端框架
2501_916008892 天前
SSE 和 gRPC 流式接口如何抓包,调试 SSE 与 gRPC 流式接口
网络协议·计算机网络·网络安全·ios·adb·https·udp