iOS 绘制一个表盘时钟,秒针效果可以“扫秒/游走”
最近自己 也尝试写了一个表盘时钟,初衷源于等车时候一个老奶奶问时间,我打开手机,时间数字对我来说相对敏感,但是老奶奶是看不清的,我想识别 还是看表盘 老远 看时针分针角度就可以识别当前时间。
于是我想写一个表盘时钟。
效果图:
基本原理,基本逻辑和其他时钟大同小异:定时器 repeat 获取当前时分秒,计算旋转角度,渲染UI。
几个注意的关键点,重点,难点。
一.旋转角度
以表盘为圆心,即 时针分针秒针绘制的矩形UI 锚点 anchorPoint. (默认锚点 是矩形中心点 anchorPoint(0.5,0.5)))
//时钟偏转角度 CGFloat hoursAngle = (components.hour / 12.0) * M_PI * 2.0; //分钟偏转角度 CGFloat minsAngle = (components.minute / 60.0) * M_PI * 2.0; //秒钟旋转角度 CGFloat secsAngle = (components.second / 60.0) * M_PI * 2.0; CGFloat prevSecAngle = ((components.second - 1) / 60.0) * M_PI * 2.0;
position 和 anchorPoint 关系:
(1)position是layer中的anchorPoint在superLayer中的位置坐标。
(2)position与anchorPoint是处于不同坐标空间中的重合点,修改重合点在一个坐标空间的位置不影响该重合点在另一个坐标空间中的位置
(3)公式
frame.origin.x = position.x - anchorPoint.x * bounds.size.width;
frame.origin.y = position.y - anchorPoint.y * bounds.size.height;
二.秒针是否“扫秒或游走秒针”
每秒一动的秒针效果:
起初使用了
self.secondHandImageV.transform = CGAffineTransformMakeRotation(secsAngle);
游走秒针使用:
//提前存储秒针layer的初始位置 [self.secondHandImageV.layer removeAnimationForKey:@"transform"]; CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"transform"]; ani.duration = 1.f; ani .removedOnCompletion= NO; //ani.delegate = self; ani.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(prevSecAngle , 0, 0, 1)]; ani.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(secsAngle , 0, 0, 1)]; [self.secondHandImageV.layer addAnimation:ani forKey:@"transform"];
因为一个事layer层的变化 一个不是,当两种秒针效果在真机中切换的时候 总会闪动 原因参见参考2
于是需要及时修改layer层的联动变化添加了
ani.delegate = self; #pragma mark -CAAnimationDelegate - (void)animationDidStart:(CAAnimation *)anim { //防止layer动画闪动 self.secondHandImageV.layer.transform = CATransform3DMakeRotation (self.secondAngel, 0, 0, 1); //NSLog(@"animationDidStart%@",self.secondHandImageV.layer.animationKeys); } - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{ //防止layer动画闪动 self.secondHandImageV.layer.transform = CATransform3DMakeRotation (self.secondAngel, 0, 0, 1); //NSLog(@"animationDidStop%@",self.secondHandImageV.layer.animationKeys); }
三.时针分针动一下时候de效果
期初都是满足条件 1秒直接跳到下一个位置,但是在“扫描/游走秒针”效果下,仿佛临界的跳动状态不具有一致性,于是在“扫描/游走秒针”状态下,时针 分针 添加一个1s de animation. 整体临界效果就自然了
整体timer 定时任务如下:
#pragma mark -- 定时任务 - (void)tick { // NSCalendarIdentifierGregorian : 指定日历的算法 NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]; // NSDateComponents封装了日期的组件,年月日时分秒等(个人感觉像是平时用的model模型) // 调用NSCalendar的components:fromDate:方法返回一个NSDateComponents对象 // 需要的参数分别components:所需要的日期单位 date:目标月份的date对象 // NSUInteger units = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;//所需要日期单位 NSDateComponents *components = [calendar components:NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:[NSDate date]]; //时钟偏转角度 CGFloat hoursAngle = (components.hour / 12.0) * M_PI * 2.0; //分钟偏转角度 CGFloat minsAngle = (components.minute / 60.0) * M_PI * 2.0; //秒钟旋转角度 CGFloat secsAngle = (components.second / 60.0) * M_PI * 2.0; CGFloat prevSecAngle = ((components.second - 1) / 60.0) * M_PI * 2.0; self.secondAngel = secsAngle ; if (self.isWanderSecond) { //提前存储秒针layer的初始位置 [self.secondHandImageV.layer removeAnimationForKey:@"transform"]; CABasicAnimation *ani = [CABasicAnimation animationWithKeyPath:@"transform"]; ani.duration = 1.f; ani .removedOnCompletion= NO; ani.delegate = self; ani.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(prevSecAngle , 0, 0, 1)]; ani.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(secsAngle , 0, 0, 1)]; [self.secondHandImageV.layer addAnimation:ani forKey:@"transform"]; } else { [self.secondHandImageV.layer removeAnimationForKey:@"transform"]; self.secondHandImageV.layer.transform = CATransform3DMakeRotation (secsAngle, 0, 0, 1); //self.secondHandImageV.transform = CGAffineTransformMakeRotation(secsAngle); } // if (self.isWanderSecond && self.isContinuous) { [UIView animateWithDuration:1.0 animations:^{ self.hourHandImageV.transform = CGAffineTransformMakeRotation(hoursAngle); self.minuteHandImageV.transform = CGAffineTransformMakeRotation(minsAngle); }]; } else { self.isContinuous = YES; self.hourHandImageV.transform = CGAffineTransformMakeRotation(hoursAngle); self.minuteHandImageV.transform = CGAffineTransformMakeRotation(minsAngle); } }
github地址 TimeClock
参考
1.https://www.jianshu.com/p/2f8962055f21 (layer层 中 position 和 anchorPoint 关系)
2. https://blog.csdn.net/mydo/article/details/51553982
posted on 2019-01-21 16:56 ACM_Someone like you 阅读(1004) 评论(0) 编辑 收藏 举报
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)