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

基于Swift实现仿IOS闹钟
仿 iOS 系统闹钟 添加闹钟效果图 收到通知效果图 更新日志 2018.09.12 由于 iOS 系统限制了注册本地推送的数量,最大的注册量为 64 条,且一旦超出 64 条,所有的推送都将失效,故而在添加推送的时候做了一个判断,超过…...

Threadlocal的实现原理
文章目录 ThreadLocal与Thread关系分析Threadlocal 不支持继承性lnheritableThreadLocal 类 ThreadLocal与Thread关系分析 由该图可知, Thread 类中有一个 threadLocals 和一个 inheritableThreadLocals , 它们 都是 ThreadLocalMap 类型 的变量 &#x…...

线程池处理异常
线程池在提交的任务在处理过程中发生了异常,却没有捕获到,导致异常只是输出在控制台,这通常需要把异常记录下来1、通过观察ThreadGroup的构造方法知道,当调用线程组的构造方法时,会获取当前线程所属的线程组࿰…...

RabbitMQ配置SSL证书
配置阿里云服务器RabbitMQ-SSL证书【windows】 文章目录 配置阿里云服务器RabbitMQ-SSL证书【windows】1. 证书下载2. 系统中添加证书(不知道是不是必要的)3. OpenSSL下载4. ca、server证书及私钥提取5. RabbitMQ-SSL证书配置6. 参考博客 1. 证书下载 进…...
.NET 9.0 的 Blazor Web App 项目,进度条 <progress> 组件使用注意事项
一、执行过程中,要刷新 进度条 的显示,需要 延时、释放,否则进度条不 实时 更新,最后一下到 100% // 延时,释放给前端:【必须】,否则进度条不 实时 更新,最后一下到 100await Task.D…...
第J7周:对于ResNeXt-50算法的思考
目录 FROM思考 FROM 🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 📌你需要解决的疑问:这个代码是否有错?对错与否都请给出你的思考 📌打卡要求:…...

【第2章:神经网络基础与实现——2.3 多层感知机(MLP)的构建与调优技巧】
在当今科技飞速发展的时代,人工智能早已不是一个陌生的词汇,它已经渗透到我们生活的方方面面,从智能语音助手到自动驾驶汽车,从图像识别到自然语言处理。而支撑这一切的核心技术之一,就是神经网络。作为机器学习领域的璀璨明星,神经网络已经在众多任务中取得了令人瞩目的…...
【Elasticsearch】keyword分析器
Elasticsearch 中的keyword分析器是一种非常特殊的分析器,它的行为与其他常见的分析器(如standard、whitespace等)截然不同。keyword分析器的核心功能是将整个输入字符串作为一个单一的标记(token)返回,而不…...

重生之我在异世界学编程之C语言:深入预处理篇(上)目录)
大家好,这里是小编的博客频道 小编的博客:就爱学编程 很高兴在CSDN这个大家庭与大家相识,希望能在这里与大家共同进步,共同收获更好的自己!!! 本文目录 引言正文一、预处理的作用与流程…...

MySQL数据库误删恢复_mysql 数据 误删
2、BigLog日志相关 2.1、检查biglog状态是否开启 声明: 当前为mysql版本5.7 当前为mysql版本5.7****当前为mysql版本5.7 2.1.1、Navicat工具执行 SHOW VARIABLES LIKE LOG_BIN%;OFF 是未开启状态,如果不是ON 开启状态需要开启为ON。{默认情况下就是关闭状态} 2.…...
SpringAI集成DeepSeek实战
SpringAI集成DeepSeek实战教程 引言 Spring AI作为Spring生态系统中的新成员,为开发者提供了便捷的AI集成方案。本文将详细介绍如何在Spring项目中集成DeepSeek模型,实现智能对话等功能。 环境准备 在开始之前,请确保您的开发环境满足以下要…...
解决 THC/THC.h: No such file or directory 报错
报错现象: cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C In file included from /data/joyiot/liyong/codes/graspnet-baseline/knn/src/knn.h:5:0,from /data/joyiot/liyong/codes/graspnet-baseline/knn/s…...

S4D480 S4HANA 基于PDF的表单打印
2022年元旦的笔记草稿 SAP的表单打印从最早的SAPScripts 到后来的SMARTFORM,步入S4时代后由于Fiori的逐渐普及,更适应Web的Adobe Form成了SAP主流output文件格式。 目录 一、 基于PDF表单打印系统架构Interface 接口Form 表单ContextLayout 二、表单接…...
数组_移除元素
数组_移除元素 一、leetcode-27二、题解1.代码2.思考 一、leetcode-27 移除元素 给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。 假设 nums 中不等于 val 的元素数…...
Vue2/Vue3分别如何使用Watch
在 Vue 2 和 Vue 3 中,watch 用于监听数据的变化并执行相应的逻辑。虽然两者的核心功能相同,但在语法和使用方式上有一些区别。以下是 Vue 2 和 Vue 3 中使用 watch 的详细说明: Vue 2 中的 watch 在 Vue 2 中,watch 是通过选项式…...

C++从入门到实战(四)C++引用与inline,nullptr
C从入门到实战(四)C引用与inline,nullptr 前言一、C 引用(一)什么是引用(二)引用的特点(三)引用作为函数参数(四)引用作为函数返回值(…...

Linux库制作与原理:【静态库】【动态库】【目标文件】【ELF文件】【ELF从形成到假造轮廓】【理解链接和加载】
目录 一.什么是库 二.静态库 2.1创建静态库 我们在之前的路径下新建lib使用我们自己的库 2.2 使用makefile生成静态库 三.动态库 3.1动态库生成 3.2动态库使用 3.3库运行搜索路径 四.目标文件 五.ELF文件 六.ELF从形成到加载轮廓 6.1ELF形成可执行 6.2 ELF可执行文…...

项目BUG
项目BUG 前言 我创作这篇博客的目的是记录学习技术过程中的笔记。希望通过分享自己的学习经历,能够帮助到那些对相关领域感兴趣或者正在学习的人们。 项目BUG 1.低频率信号(100k或 200K以下)可以直接用一根导线焊接出几根导线来分几路,高频率信号只能…...
wordpress部署nginx版的
一、通过nginx部署wordpress 1、用yum源安装nginx yum install -y nginx 2、安装php相关软件 前提安装webtatic rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm 通过yum源安装php相关软件 yum -y install php72w php72w-pdo php72w-mysqlnd php72w…...
【鸿蒙Next】优秀鸿蒙博客集锦
鸿蒙基础开发:多文件压缩上传及断点续传_鸿蒙 断点续传-CSDN博客...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...

黑马Mybatis
Mybatis 表现层:页面展示 业务层:逻辑处理 持久层:持久数据化保存 在这里插入图片描述 Mybatis快速入门 
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练
前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1):从基础到实战的深度解析-CSDN博客,但实际面试中,企业更关注候选人对复杂场景的应对能力(如多设备并发扫描、低功耗与高发现率的平衡)和前沿技术的…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:
根据万维钢精英日课6的内容,使用AI(2025)可以参考以下方法: 四个洞见 模型已经比人聪明:以ChatGPT o3为代表的AI非常强大,能运用高级理论解释道理、引用最新学术论文,生成对顶尖科学家都有用的…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...

算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

消息队列系统设计与实践全解析
文章目录 🚀 消息队列系统设计与实践全解析🔍 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡💡 权衡决策框架 1.3 运维复杂度评估🔧 运维成本降低策略 🏗️ 二、典型架构设计2.1 分布式事务最终一致…...