Swift5.2 Control CountDownManager(时间计时器)

发布时间:2026/10/11 2:14:46

Swift5.2 Control CountDownManager(时间计时器) 一直觉得自己写的不是技术而是情怀一个个的教程是自己这一路走来的痕迹。靠专业技能的成功是最具可复制性的希望我的这条路能让你们少走弯路希望我能帮你们抹去知识的蒙尘希望我能帮你们理清知识的脉络希望未来技术之巅上有你们也有我。Swift-CountDownManager(时间计时器)代码下载使用场景当时写项目的时候用户进入确认订单后退出变成待支付计算用户未付款的时候超过时间后取消订单。效果展示代码使用代码importUIKitimportSnapKitclassViewController:UIViewController{privateletcountDownManagerCountDownManager()privatelettimeLabelUILabel()overridefuncviewDidLoad(){super.viewDidLoad()view.backgroundColor.whitesetupUI()startClockwiseTimer()}privatefuncsetupUI(){timeLabel.textAlignment.center timeLabel.fontUIFont.systemFont(ofSize:16,weight:.medium)timeLabel.textColor.black view.addSubview(timeLabel)timeLabel.snp.makeConstraints{makeinmake.center.equalToSuperview()}}privatefuncstartClockwiseTimer(){// 假设开始时间是 2026-08-18 10:00:00 的时间戳毫秒letstartTimestamp:Int641_720_000_000_000// 示例值请替换为实际值// 时间校准如果需要与服务器时间同步传入偏差值否则传 0letadjust:TimeInterval0countDownManager.clockwiseHasPasstimeWithStratTimeStamp(startTimestamp,adjust:adjust){[weakself]timeStringinself?.timeLabel.text已过时间\(timeString)}}}封装代码importFoundation class CountDownManager{private var timer: DispatchSourceTimer? privateletdateFormatter: DateFormatter{letformatterDateFormatter()formatter.dateFormatYYYY-MM-dd HH:mm:ssformatter.timeZoneTimeZone.currentreturnformatter}()deinit{destoryTimer()print(\(Self.self) deinit)}// MARK: - Public Methods /// 顺计时 /// - Parameters: /// - starTimeStamp: 开始时间戳(毫秒)/// - adjust: 时间校准值(秒)/// - completeBlock: 回调(已过时间字符串)func clockwiseHasPasstimeWithStratTimeStamp(_ starTimeStamp: Int64, adjust: TimeInterval, completeBlock: escaping(String)-Void){destoryTimer()letnowIntervalDate().timeIntervalSince1970 adjustletpassIntervalnowInterval - TimeInterval(starTimeStamp)/1000vartimeoutInt(passInterval)timerDispatchSource.makeTimerSource(queue: DispatchQueue.global())timer?.schedule(deadline: .now(), repeating:1.0)timer?.setEventHandler{[weak self]intimeout1lethourstimeout/(60*60)letminutes(timeout %(60*60))/60letsecondstimeout%60DispatchQueue.main.async{completeBlock(self?.refreshUI(hour: hours, minute: minutes, second: seconds)??)}}timer?.resume()}/// 计算已过时间 /// - Parameters: /// - starTimeStamp: 开始时间戳(毫秒)/// - finishTimeStamp: 结束时间戳(毫秒)/// - adjust: 时间校准值(秒)/// - completeBlock: 回调(已过时间字符串, 是否结束)func countDownHasPasstimeWithStratTimeStamp(_ starTimeStamp: Int64, finishTimeStamp: Int64, adjust: TimeInterval, completeBlock: escaping(String, Bool)-Void){destoryTimer()letnowIntervalDate().timeIntervalSince1970 adjustletpassIntervalnowInterval - TimeInterval(starTimeStamp)/1000letfinishIntervalTimeInterval(finishTimeStamp - starTimeStamp)/1000vartimeoutInt(passInterval)iftimeoutInt(finishInterval){timerDispatchSource.makeTimerSource(queue: DispatchQueue.global())timer?.schedule(deadline: .now(), repeating:1.0)timer?.setEventHandler{[weak self]invar isFinishfalseiftimeoutInt(finishInterval){self?.destoryTimer()isFinishtrue}else{timeout1}lethourstimeout/(60*60)letminutes(timeout %(60*60))/60letsecondstimeout%60DispatchQueue.main.async{completeBlock(self?.refreshUI(hour: hours, minute: minutes, second: seconds)??, isFinish)}}timer?.resume()}}/// 根据结束时间倒计时 /// - Parameters: /// - finishTimeStamp: 结束时间戳(毫秒)/// - adjust: 时间校准值(秒)/// - completeBlock: 回调(剩余时间字符串, 是否结束)func countDownWithFinishTimeStamp(_ finishTimeStamp: Int64, adjust: TimeInterval, completeBlock: escaping(String, Bool)-Void){letnowIntervalDate().timeIntervalSince1970 adjustletlastTimeIntervalTimeInterval(finishTimeStamp)/1000- nowInterval countDownWithTimeout(lastTimeInterval, completeBlock: completeBlock)}// MARK: - Private Methods private func countDownWithTimeout(_ timeOut: TimeInterval, completeBlock: escaping(String, Bool)-Void){destoryTimer()vartimeoutInt(timeOut)iftimeout0{timerDispatchSource.makeTimerSource(queue: DispatchQueue.global())timer?.schedule(deadline: .now(), repeating:1.0)timer?.setEventHandler{[weak self]iniftimeout0{self?.destoryTimer()DispatchQueue.main.async{completeBlock(self?.refreshUI(hour:0, minute:0, second:0)??,true)}}else{lethourstimeout/(60*60)letminutes(timeout %(60*60))/60letsecondstimeout%60DispatchQueue.main.async{completeBlock(self?.refreshUI(hour: hours, minute: minutes, second: seconds)??,false)}timeout-1}}timer?.resume()}else{completeBlock(refreshUI(hour:0, minute:0, second:0),true)}}private func refreshUI(hour: Int, minute: Int, second: Int)-String{var timeStrifhour0{timeStrString(format:%02d小时, hour)}timeStrString(format:%02d分钟, minute)timeStrString(format:%02d秒, second)returntimeStr}private func getNowyyyymmdd()-String{letnowDate()letformatDayDateFormatter()formatDay.dateFormatyyyy-MM-ddreturnformatDay.string(from: now)}/// 销毁定时器 funcdestoryTimer(){timer?.cancel()timernil}}
延伸阅读

更多相关文章

2026/10/10 23:40:55

羽毛球篮球约球小程序开发方案

羽毛球篮球约球小程序开发方案羽毛球、篮球是同城大众参与度最高的两类球类运动,二者组队规则、场地规格、参与人数、对局模式存在明显差异化特性。篮球偏向多人对抗组队,对局人数固定、团队配合要求高;羽毛球多为双人对战、四人双打&#xf…

2026/10/8 20:55:15

四层递归自指认知架构中自描述子系统(ADS)的角色研究

四层递归自指认知架构中自描述子系统(ADS)的角色研究 作者:方见华 单位:世毫九实验室 摘要 世毫九实验室(SH9)提出的四层递归自指认知架构,是突破传统AI“无内生自我认知、依赖外部人工标签、人…

2026/10/11 2:12:29

双指针算法详解:对撞、快慢与滑动窗口实战

1. 双指针到底解决了什么问题先从一个最常见的场景说起。假设你拿到了一个有序数组,要找出其中两个数,使它们的和等于某个目标值。最直接的想法是两层循环暴力枚举,第一层固定一个数,第二层遍历剩下的数去检查。这个方案的复杂度是…

2026/10/11 2:12:29

EtherCAT网关选型全攻略:协议、芯片、线缆三大关键

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 2:12:29

Docker搭建Redis集群实战:从主从复制到哨兵模式与Cluster分片

1. 为什么要用Docker搭Redis集群:从单节点到主从哨兵的演进逻辑先说一个很多人问我的问题:单机Redis用得好好的,为什么要费劲搭集群?这个问题其实分两层。第一层,如果你的业务量还没到单节点扛不住的程度,那…

2026/10/11 2:12:29

段式存储管理地址转换:一道408真题拆解段表与越界判断

1. 考点定位与题目背景1.1 这道题为什么值得反复咀嚼如果你准备过408统考,应该有这样的体会:操作系统里内存管理章节,概念多、原理杂,但真正落到计算题上,考来考去就是地址变换。段式管理、页式管理、段页式管理&#…

2026/10/11 2:12:29

华为鸿蒙免费外卖记录工具—小羊外卖

名字容易误会:小羊外卖不接单、不送餐,只帮你把点过的留下来。盼你装上,先记最近一顿——下次饭点纠结,可以从自己的味道里抽一家。先说重点① 纯净、无广告、不收费② 记餐厅、菜品、价钱、评分,还能拍小票、复制上次…

2026/10/11 2:07:29

嵌入式Bug排查实战指南:从日志法到示波器的系统化调试思路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

2026/10/11 0:02:13

Python调用Gemini Structured Outputs实现工单路由门禁

客服工单最怕的不是模型“答错一句话”,而是它给出一段看起来合理的说明,程序却从中猜错优先级。通俗做法是:要求模型只交 JSON(JavaScript Object Notation,轻量数据格式),再让代码验证它。Gem…

2026/10/11 0:02:13

Spring Boot超市进销存系统毕设实战:从需求拆解到答辩通关

最近带的一个学生项目组里,有A同学跑来问我:选什么毕设题目最稳妥,既能让评审老师觉得工作量够,又不会在答辩时被问到语无伦次。我第一反应就是推荐基于Spring Boot的超市仓库管理系统——也就是超市进销存系统。这个题目乍一看平…

2026/10/11 0:02:13

Flutter StatefulWidget 生命周期核心解析

很多刚开始接触 Flutter 的朋友,在看完一堆“Hello World”和基础组件之后,大概率都会撞上同一堵墙:StatefulWidget 里那堆 initState、build、dispose 方法,到底什么时候被调用?为什么顺序是那样?在里面到…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑