基于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博客...
手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
Element Plus 表单(el-form)中关于正整数输入的校验规则
目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入(联动)2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
【JVM】Java虚拟机(二)——垃圾回收
目录 一、如何判断对象可以回收 (一)引用计数法 (二)可达性分析算法 二、垃圾回收算法 (一)标记清除 (二)标记整理 (三)复制 (四ÿ…...
【从零开始学习JVM | 第四篇】类加载器和双亲委派机制(高频面试题)
前言: 双亲委派机制对于面试这块来说非常重要,在实际开发中也是经常遇见需要打破双亲委派的需求,今天我们一起来探索一下什么是双亲委派机制,在此之前我们先介绍一下类的加载器。 目录 编辑 前言: 类加载器 1. …...
