美文网首页
iOS 旋转倒计时解决方案

iOS 旋转倒计时解决方案

作者: Attu_7 | 来源:发表于2018-02-26 11:18 被阅读0次

产品中需要使用到一个带旋转动画的倒计时按钮。但在开发过程中产生了两个新的需求。一、APP进入后台时倒计时暂停,返回前台继续倒计时。二、在倒计时未结束的时候,移除该页面,此Button自动dealloc掉。(如果不dealloc,等到动画完成后由系统去dealloc,会触发此动画结束的回调,执行回调中的方法,而此时该页面已经移除了,会引起各种问题。)

那么先摆一下我踩的坑,最后给上我自己的解决方案。(如果有直接想看最终方案的童鞋,可到文尾,有传送门。)

第一种思路,使用Animation解决。

这是最先想到的方法,使用贝塞尔曲线画一个圆,再用CABaseAnimation做倒计时动画。实现细节我这边就不贴代码了,网上有很多这样的代码。
说一下这样解决的一个问题。使用CABaseAnimation做动画,我们很自然的使用

- (void)animationDidStop:(CAAnimation*)anim finished:(BOOL)flag {
    //执行动画结束回调  
}

来截获动画结束时的回调,再传递到外层给做各种结束处理。但这样处理就会引发我一开始说的第一个问题,在APP进入后台时,系统会自动remove掉所有的Animation,就会触发 animationDidStop 这个函数,而此时我们并不需要触发它。再而返回前台后,这个动画就结束了。
我尝试了一些解决方法,在回到前台是重新开始动画等。但都没有很好的解决这个问题。

第二种思路,重绘圆环。

不使用Animation,就只能自己定义NSTimer来实现该功能。核心代码如下

self.progressValue = 0;
timer = [NSTimer at_scheduledTimerWithTimeInterval:0.03 block:^{    
    if (floor(self.progressValue) > 1.0f) {
        if (timer) {
            [timer invalidate];
            timer = nil;
        }
        return;
    } else {
        frontFillBezierPath = [UIBezierPath bezierPathWithArcCenter:center radius:(CGRectGetWidth(wSelf.bounds)-progressStrokeWidth)/2.f startAngle:-M_PI_2 endAngle:(2*M_PI)*self.progressValue-M_PI_2 clockwise:YES];
        frontFillLayer.path = frontFillBezierPath.CGPath;
    }
    self.progressValue+=(0.03/2);   
} repeats:YES];

该方案的核心就是每0.03秒重绘一次贝塞尔曲线的圆弧,由于人的视觉残留现象,形成一个连续的动画。虽然这样对圆环的暂停,重启更加方便。也解决了文章开始第一条说的问题,但由于每0.03秒需要重绘一次,比较消耗CPU。如果每0.1s执行一次,就无法形成连续的动画。一般每秒要24-25帧,人眼才能觉得不卡顿。

第三种思路,利用strokeEnd自带的动画属性。

CALayer有两个属性strokeStart和strokeEnd,分别表示起始位置和结束为止,默认支持动画,这两个属性的值在0~1之间。strokeStart=0.1f; strokeEnd=0.7f则显示如下图所示。

图片来自网络(侵删)

通过改变strokeEnd属性的值来实现倒计时的动画效果,核心代码如下:

CGFloat timeInterval = 0.1f;
CGFloat percent = 1.0f/(duration/timeInterval);

__weak typeof(self) weakSelf = self;
self.timer = [NSTimer at_scheduledTimerWithTimeInterval:timeInterval repeats:YES block:^(NSTimer * _Nonnull timer) {
    if (weakSelf.frontFillLayer.strokeEnd >= 1) {
        [weakSelf stop];
    } else {
        weakSelf.frontFillLayer.strokeEnd += percent;
        if (block) {
            block(weakSelf.frontFillLayer.strokeEnd * duration);
        }
    }
}];

有一点需要注意,strokeStart默认的起始位置为3点钟方向。如果想改变起始位置为12点钟方向,把strokeStart设为-0.25是没有效果的。(它的取值范围是0-1)只需要一开始画贝塞尔曲线时,将起始位置设为-M_PI_2。

self.frontFillBezierPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:-M_PI_2 endAngle:1.5*M_PI clockwise:YES];

另外在dealloc时,手动销毁定时器,就解决了文章开头说的第二个问题了。

最后给上Demo地址,传送门

相关文章

网友评论

      本文标题:iOS 旋转倒计时解决方案

      本文链接:https://www.haomeiwen.com/subject/zlfexftx.html