最近自己 也尝试写了一个表盘时钟,初衷源于等车时候一个老奶奶问时间,我打开手机,时间数字对我来说相对敏感,但是老奶奶是看不清的,我想识别 还是看表盘 老远 看时针分针角度就可以识别当前时间。
于是我想写一个表盘时钟。
效果图:
基本原理,基本逻辑和其他时钟大同小异:定时器 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);
}
}
参考
1.https://www.jianshu.com/p/2f8962055f21 (layer层 中 position 和 anchorPoint 关系)
2. https://blog.csdn.net/mydo/article/details/51553982