最近自己 也尝试写了一个表盘时钟,初衷源于等车时候一个老奶奶问时间,我打开手机,时间数字对我来说相对敏感,但是老奶奶是看不清的,我想识别 还是看表盘 老远 看时针分针角度就可以识别当前时间。

于是我想写一个表盘时钟。

效果图:

基本原理,基本逻辑和其他时钟大同小异:定时器 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

iOS 绘制一个表盘时钟,秒针效果可以“扫秒/游走”的更多相关文章

  1. 用canvas绘制一个简易时钟

    在见识了html5中canvas的强大,笔者准备制作一个简易时钟. 下面就是成果啦,制作之前我们先分析一下,绘制一个时钟需要做哪些准备. 一 . 1.首先这个时钟分为表盘,指针(时针,分针,秒针)和数 ...

  2. 转:iOS绘制一个UIView

    绘制一个UIView 绘制一个UIVIew最灵活的方式就是由它自己完成绘制.实际上你不是绘制一个UIView,你只是子类化了UIView并赋予子类绘制自己的能力.当一个UIVIew需要执行绘图操作的时 ...

  3. IOS中一个简单的粒子效果实现

    1.效果图展示 2.实现思路 1> 首先要实现上面的效果,第一步要处理的就是一个简单的画板,在View上面用鼠标滑动的时候画出线条,这个功能可使用UIBezierPath实现 2> 关于粒 ...

  4. 使用canvas绘制一个时钟

    周末学习canvas的一些基础功能,顺带写了一个基础的时钟.现在加工一下,做的更好看一点,先放上效果图: 谈一些自己的理解: (1).要绘制一个新的样式(不想被其他样式影响,或者影响到其他样式),那么 ...

  5. iOS时钟,秒针扫秒样式

    昨天做一个时钟小demo,发现了一些问题. 描述能力有限,我封装好了一个时钟框架,朋友们可以参考      https://github.com/qianlishun/ClockView 点击这里可以 ...

  6. IOS 中openGL使用教程2(openGL ES 入门篇 | 绘制一个多边形)

    在上一篇我们学习了如何搭建IOS下openGL的开发环境,接下来我们来学习如何绘制一个多边形. 在2.0之前,es的渲染采用的是固定管线,何为固定管线,就是一套固定的模板流程,局部坐标变换 -> ...

  7. 利用canvas画一个实时时钟

    先放一张效果图: 下面是源代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"& ...

  8. iOS----自定义UIView,绘制一个UIView

    绘制一个UIVIew最灵活的方式就是由它自己完成绘制.实际上你不是绘制一个UIView,你只是子类化了UIView并赋予子类绘制自己的能力.当一个UIVIew需要执行绘图操作的时,drawRect:方 ...

  9. 高仿IOS下拉刷新的粘虫效果

    最近看需要做一款下拉刷新的效果,由于需要和Ios界面保持一致,所以这用安卓的方式实现了ios下的下拉刷新的粘虫效果. 最新的安卓手机版本的QQ也有这种类似的效果,就是拖动未读信息的那个红色圆圈,拖动近 ...

随机推荐

  1. 在化学教学中怎么用ChemOffice

    在21世纪新课程化学教学中,有很多地方要求化学教师使用计算机和应用程序处理各类化学信息,比如课堂教学.撰写教学论文.制作多媒体课件等,这样可以有效增强教学效果.作为化学教师,在新课程的化学教学中掌握一 ...

  2. ReactNative iOS源码解析

    http://awhisper.github.io/2016/06/24/ReactNative%E6%B5%81%E7%A8%8B%E6%BA%90%E7%A0%81%E5%88%86%E6%9E% ...

  3. 转:解决Python2.7的UnicodeEncodeError: ‘ascii’ codec can’t encode异常错误

    操作SQL数据库,Python使用的是版本2.7,但是在运行的时候出现了异常错误UnicodeEncodeError:'ascii' codec can't encode characters in ...

  4. Struts2_day04--课程介绍_Struts2拦截器概述&底层原理_重要的概念

    Struts2_day04 上节内容 今天内容 Struts2拦截器概述 拦截器底层原理 重要的概念 自定义拦截器 自定义登录拦截器 Struts2的标签库 Struts2表单标签(会用) Strut ...

  5. vue2.0非父子间进行通讯

    在vue中,父组件向之组件通讯使用的是props,子组件向父组件通讯使用的是$emit+事件,那非父子间的通讯呢,在官方文档上只有寥寥数笔, 概念很模糊,这个空的vue实例应该放在哪里呢,光放文档并没 ...

  6. IOS7开发~新UI学起(二)

    本文转载至 http://blog.csdn.net/lizhongfu2013/article/details/9133281 1.UINavigationBar: NSDictionary* at ...

  7. BaseAdapter<T> 重写 createViewFromResource实现界面,刷新,加载,移除

    import java.util.ArrayList; import java.util.List; import android.content.Context; import android.vi ...

  8. android how to deal with data when listview refresh

    如何解决listview数据刷新,下拉刷新,上拉加载更多时,图片不闪烁. 在Activity的onResume()方法中将adaper和listView重新再绑定一次. listView.setAda ...

  9. angular 2+ 路由守卫

    1. 定义接口名称 /domain/login-guard.ts export interface LoginGuard { data: any; msg: string; status: boole ...

  10. 理解CSS3 isolation: isolate的表现和作用

    转自:http://www.zhangxinxu.com/wordpress/?p=5155 只要元素可以创建层叠上下文,就可以阻断mix-blend-mode! 于是,不仅仅是isolation:i ...