產(chǎn)品中需要使用到一個(gè)帶旋轉(zhuǎn)動(dòng)畫的倒計(jì)時(shí)按鈕拷恨。但在開發(fā)過程中產(chǎn)生了兩個(gè)新的需求。一谢肾、APP進(jìn)入后臺時(shí)倒計(jì)時(shí)暫停,返回前臺繼續(xù)倒計(jì)時(shí)小泉。二芦疏、在倒計(jì)時(shí)未結(jié)束的時(shí)候冕杠,移除該頁面,此Button自動(dòng)dealloc掉酸茴。(如果不dealloc分预,等到動(dòng)畫完成后由系統(tǒng)去dealloc,會(huì)觸發(fā)此動(dòng)畫結(jié)束的回調(diào)薪捍,執(zhí)行回調(diào)中的方法笼痹,而此時(shí)該頁面已經(jīng)移除了,會(huì)引起各種問題酪穿。)
那么先擺一下我踩的坑凳干,最后給上我自己的解決方案。(如果有直接想看最終方案的童鞋被济,可到文尾救赐,有傳送門。)
第一種思路只磷,使用Animation解決经磅。
這是最先想到的方法,使用貝塞爾曲線畫一個(gè)圓钮追,再用CABaseAnimation做倒計(jì)時(shí)動(dòng)畫预厌。實(shí)現(xiàn)細(xì)節(jié)我這邊就不貼代碼了,網(wǎng)上有很多這樣的代碼元媚。
說一下這樣解決的一個(gè)問題轧叽。使用CABaseAnimation做動(dòng)畫,我們很自然的使用
- (void)animationDidStop:(CAAnimation*)anim finished:(BOOL)flag {
//執(zhí)行動(dòng)畫結(jié)束回調(diào)
}
來截獲動(dòng)畫結(jié)束時(shí)的回調(diào)惠毁,再傳遞到外層給做各種結(jié)束處理犹芹。但這樣處理就會(huì)引發(fā)我一開始說的第一個(gè)問題,在APP進(jìn)入后臺時(shí)鞠绰,系統(tǒng)會(huì)自動(dòng)remove掉所有的Animation腰埂,就會(huì)觸發(fā) animationDidStop 這個(gè)函數(shù),而此時(shí)我們并不需要觸發(fā)它蜈膨。再而返回前臺后屿笼,這個(gè)動(dòng)畫就結(jié)束了。
我嘗試了一些解決方法翁巍,在回到前臺是重新開始動(dòng)畫等驴一。但都沒有很好的解決這個(gè)問題。
第二種思路灶壶,重繪圓環(huán)肝断。
不使用Animation,就只能自己定義NSTimer來實(shí)現(xiàn)該功能。核心代碼如下
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秒重繪一次貝塞爾曲線的圓弧胸懈,由于人的視覺殘留現(xiàn)象担扑,形成一個(gè)連續(xù)的動(dòng)畫。雖然這樣對圓環(huán)的暫停趣钱,重啟更加方便涌献。也解決了文章開始第一條說的問題,但由于每0.03秒需要重繪一次首有,比較消耗CPU燕垃。如果每0.1s執(zhí)行一次,就無法形成連續(xù)的動(dòng)畫井联。一般每秒要24-25幀卜壕,人眼才能覺得不卡頓。
第三種思路低矮,利用strokeEnd自帶的動(dòng)畫屬性印叁。
CALayer有兩個(gè)屬性strokeStart和strokeEnd,分別表示起始位置和結(jié)束為止军掂,默認(rèn)支持動(dòng)畫轮蜕,這兩個(gè)屬性的值在0~1之間。strokeStart=0.1f; strokeEnd=0.7f則顯示如下圖所示蝗锥。
通過改變strokeEnd屬性的值來實(shí)現(xiàn)倒計(jì)時(shí)的動(dòng)畫效果跃洛,核心代碼如下:
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);
}
}
}];
有一點(diǎn)需要注意,strokeStart默認(rèn)的起始位置為3點(diǎn)鐘方向终议。如果想改變起始位置為12點(diǎn)鐘方向汇竭,把strokeStart設(shè)為-0.25是沒有效果的。(它的取值范圍是0-1)只需要一開始畫貝塞爾曲線時(shí)穴张,將起始位置設(shè)為-M_PI_2细燎。
self.frontFillBezierPath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:-M_PI_2 endAngle:1.5*M_PI clockwise:YES];
另外在dealloc時(shí),手動(dòng)銷毀定時(shí)器皂甘,就解決了文章開頭說的第二個(gè)問題了玻驻。
最后給上Demo地址,傳送門偿枕。