給iOS圖層添加動(dòng)畫

核心動(dòng)畫可以在layer上創(chuàng)造復(fù)雜的動(dòng)畫,比如修改大小、位置、旋轉(zhuǎn)變換等等,也可以同時(shí)修改一個(gè)或多個(gè)屬性。

簡(jiǎn)單修改layer屬性做動(dòng)畫

首先理解下隱式動(dòng)畫和顯示動(dòng)畫。當(dāng)修改圖層任意屬性時(shí)都會(huì)出發(fā)隱式動(dòng)畫,隱式動(dòng)畫有默認(rèn)時(shí)間步調(diào)和其他屬性。如果不使用系統(tǒng)的默認(rèn)動(dòng)畫可以通過(guò)創(chuàng)建CABasicAnimation對(duì)象指定圖層的顯示動(dòng)畫。調(diào)用addAnimation:forKey:方法把動(dòng)畫添加到圖層上

在之前的矢量圖上添加一個(gè)縮放的動(dòng)畫

CAShapeLayer *layer1 = [CAShapeLayer layer];
layer1.bounds = CGRectMake(0, 0, 200, 200);
layer1.position = self.view.center;
layer1.backgroundColor = [UIColor colorWithRed:0.9 green:0.3 blue:0.3 alpha:0.3].CGColor;
    
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(50, 0)];
[path addCurveToPoint:CGPointMake(0, 50)
       controlPoint1:CGPointMake(50, 25)
       controlPoint2:CGPointMake(25, 50)];
    
[path addCurveToPoint:CGPointMake(-50, 0)
       controlPoint1:CGPointMake(-25, 50)
       controlPoint2:CGPointMake(-50, 25)];
    
[path addCurveToPoint:CGPointMake(0, -50)
       controlPoint1:CGPointMake(-50, -25)
       controlPoint2:CGPointMake(-25, -50)];
    
[path addCurveToPoint:CGPointMake(50, 0)
       controlPoint1:CGPointMake(25, -50)
       controlPoint2:CGPointMake(50, -25)];
CGAffineTransform transform = CGAffineTransformMakeTranslation(100, 100);
[path applyTransform:transform];
    
layer1.path = path.CGPath;
layer1.fillColor = [UIColor whiteColor].CGColor;
layer1.strokeColor = [UIColor blueColor].CGColor;
    
[self.view.layer addSublayer:layer1];
    
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform"];
animation.fromValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.5, 1.5, 1)];
animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.2, 0.2, 1)];
animation.duration = 4.0;
[layer1 addAnimation:animation forKey:@"animation1"];

<img src="http://upload-images.jianshu.io/upload_images/1840221-e8152fe11bd3915a.gif?imageMogr2/auto-orient/strip" width=250>

要注意,顯式動(dòng)畫只是提供一個(gè)動(dòng)畫,它并不會(huì)修改圖層對(duì)象的數(shù)據(jù)。所以在動(dòng)畫結(jié)束時(shí),需要更新圖層對(duì)象的數(shù)據(jù)。

動(dòng)畫會(huì)在runloop結(jié)束時(shí)開(kāi)始執(zhí)行,因此當(dāng)前線程必須要有一個(gè)runloop才可以讓動(dòng)畫執(zhí)行。即便改變圖層的多個(gè)屬性或是添加多個(gè)動(dòng)畫,他們都會(huì)在同一時(shí)刻執(zhí)行。比如你可以在移動(dòng)的時(shí)候同時(shí)修改圖層的不透明度。雖然他們是同時(shí)執(zhí)行的,但是你可以單獨(dú)配置每個(gè)屬性的動(dòng)畫對(duì)象,控制他們屬性變化的時(shí)間節(jié)奏。

用關(guān)鍵幀動(dòng)畫修改layer屬性

關(guān)鍵幀動(dòng)畫可以在一個(gè)動(dòng)畫周期內(nèi)給屬性設(shè)置多個(gè)值。關(guān)鍵幀動(dòng)畫由一系列的值和每?jī)蓚€(gè)值之間變化時(shí)的時(shí)間節(jié)奏組成,他們都用數(shù)組表示。

CAKeyframeAnimation *keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
keyframeAnimation.keyTimes = @[@(0),@(0.3),@(0.5),@(1)];
keyframeAnimation.values = @[[NSValue valueWithCATransform3D:CATransform3DMakeScale(1.5, 1.5, 1)],
                            [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.2, 1.2, 1)],
                            [NSValue valueWithCATransform3D:CATransform3DMakeScale(1.2, 0.2, 1)],
                            [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.3, 1.2, 1)]];
keyframeAnimation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],
                                     [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                     [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut],
                                     [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
keyframeAnimation.duration = 8.f;
keyframeAnimation.fillMode = kCAFillModeForwards;
keyframeAnimation.removedOnCompletion = NO;
[layer1 addAnimation:keyframeAnimation forKey:@"transform2"];

<img src="http://upload-images.jianshu.io/upload_images/1840221-e58e4631a46e29fd.gif?imageMogr2/auto-orient/strip" width=250>

動(dòng)畫屬性keyPath

在第一行創(chuàng)建關(guān)鍵幀動(dòng)畫時(shí)調(diào)用方法animationWithKeyPath,并傳入動(dòng)畫屬性keypath。CAPropertyAnimation定義了動(dòng)畫的屬性值,它支持很多屬性,具體可以查閱官方文檔。

關(guān)鍵幀的值

通過(guò)動(dòng)畫的path或values來(lái)設(shè)置關(guān)鍵幀的值。屬性是CGPoint類型的(錨點(diǎn)或位置)通常用動(dòng)畫的path屬性來(lái)設(shè)置,path是CGPathRef類型。也可以用values屬性來(lái)設(shè)置,它是數(shù)組類型。這時(shí)需要把CGPoint類型用NSValue來(lái)進(jìn)行封裝再添加到數(shù)組中,同樣的CATransform3D和CGRect類型用NSValue封裝、 CGFloat類型用NSNumber封裝。

關(guān)鍵幀之間的節(jié)奏

關(guān)鍵幀動(dòng)畫的時(shí)間節(jié)奏和插值是通過(guò)一系列屬性共同控制的:

  • calculationMode:動(dòng)畫中的計(jì)算算法,這個(gè)屬性同時(shí)影響其他屬性的使用
    • kCAAnimationLinear 或 kCAAnimationCubic,關(guān)鍵幀之間使用直線或圓滑曲線的插值算法實(shí)現(xiàn)。 當(dāng)設(shè)置為kCAAnimationCubic時(shí),可通過(guò)參數(shù)調(diào)整差值曲線函數(shù)來(lái)完全控制中間幀變化方法。
    • kCAAnimationPaced or kCAAnimationCubicPaced,使keyTimes和timingFunctions屬性失效。動(dòng)畫平滑過(guò)渡,也就是勻速進(jìn)行。
    • kCAAnimationDiscrete,使timingFunctions屬性失效。動(dòng)畫在關(guān)鍵幀之間跳躍進(jìn)行,也就是中間幀不進(jìn)行差值計(jì)算,直接從兩個(gè)關(guān)鍵幀變化。
  • keyTimes 指定每一個(gè)關(guān)鍵幀的屬性值
  • timingFunctions 指定每?jī)蓚€(gè)關(guān)鍵幀之間過(guò)度的時(shí)間曲線變化

動(dòng)畫協(xié)議

你會(huì)發(fā)現(xiàn)第一個(gè)demo動(dòng)畫結(jié)束時(shí),layer回到了初始狀態(tài),而第二個(gè)demo在開(kāi)始和結(jié)束時(shí)layer保持動(dòng)畫開(kāi)始和結(jié)束的狀態(tài)。因?yàn)槲以O(shè)置了fillMode和removedOnCompletion這兩個(gè)屬性。layer的transform值始終都是CATransform3DIdentity,但是設(shè)置了這兩個(gè)屬性后可以讓圖層動(dòng)畫開(kāi)始時(shí)就渲染到CATransform3DMakeScale(1.5, 1.5, 1),結(jié)束時(shí)渲染到CATransform3DMakeScale(0.3, 1.2, 1)。

CAMediaTiming協(xié)議是一套抽象接口,描述了動(dòng)畫的一些信息,CALayer和CAAnimation都實(shí)現(xiàn)了該協(xié)議

  • beginTime 相對(duì)于父圖層或動(dòng)畫的開(kāi)始時(shí)間
  • duration 動(dòng)畫的單次時(shí)長(zhǎng)
  • speed 動(dòng)畫的播放速率,默認(rèn)是1
  • timeOffset 動(dòng)畫的時(shí)間偏移,就是從哪里開(kāi)始播放,默認(rèn)是0
  • repeatCount 動(dòng)畫的重復(fù)播放次數(shù),默認(rèn)是0
  • repeatDuration 動(dòng)畫的有效時(shí)長(zhǎng),默認(rèn)是0。結(jié)合重復(fù)次數(shù)使用
  • autoreverses 如果是true,動(dòng)畫正播后再進(jìn)行反播。默認(rèn)是false
  • fillMode 動(dòng)畫的填充模式,這就是上面用到的。可以四種情況:渲染開(kāi)始和結(jié)束維持layer的屬性值、在動(dòng)畫結(jié)束時(shí)layer渲染到動(dòng)畫結(jié)束時(shí)的屬性、在動(dòng)畫開(kāi)始時(shí)layer渲染到動(dòng)畫開(kāi)始時(shí)的屬性、開(kāi)始結(jié)束都渲染。

removedOnCompletion是CAAnimation的屬性,它表示動(dòng)畫一旦結(jié)束渲染就停止,所以第一個(gè)demo看到動(dòng)畫結(jié)束時(shí)layer又變回了初始的樣子。

運(yùn)行時(shí)停止動(dòng)畫

動(dòng)畫通常會(huì)一直運(yùn)行到結(jié)束,如果想提前中止有兩種方法:

  • 移除一個(gè)動(dòng)畫調(diào)用圖層的removeAnimationForKey:方法,參數(shù)和添加動(dòng)畫時(shí)addAnimation:forKey:的第二個(gè)參數(shù)相同,都不能為nil
  • 移除全部動(dòng)畫調(diào)用圖層的removeAllAnimations方法,它會(huì)馬上中止所有正在進(jìn)行的動(dòng)畫,并對(duì)layer進(jìn)行重繪。但這個(gè)方法最好不要用!

當(dāng)從圖層移除一個(gè)動(dòng)畫的時(shí)候,會(huì)出發(fā)圖層的重新繪制。圖層會(huì)跳躍到動(dòng)畫之前狀態(tài),如果想保持最后一幀的狀態(tài)可以通過(guò)修改圖層屬性實(shí)現(xiàn)。

動(dòng)畫組合

如果想圖層一直執(zhí)行多個(gè)動(dòng)畫,可以創(chuàng)建CAAnimationGroup對(duì)象通過(guò)配置animations屬性來(lái)實(shí)現(xiàn)。動(dòng)畫組的的事件節(jié)奏和時(shí)長(zhǎng)屬性會(huì)覆蓋掉所包含的動(dòng)畫屬性。

檢測(cè)動(dòng)畫結(jié)束

當(dāng)動(dòng)畫開(kāi)始和結(jié)束時(shí),可以通過(guò)回調(diào)得知:

  • 對(duì)動(dòng)畫事物CATransaction添加setCompletionBlock:方法,在動(dòng)畫結(jié)束時(shí)調(diào)用
  • 動(dòng)畫對(duì)象實(shí)現(xiàn)animationDidStart:和animationDidStop:finished:方法

如果你想連接兩個(gè)動(dòng)畫盡量不要用這種方式,通過(guò)設(shè)置動(dòng)畫的beginTime來(lái)實(shí)現(xiàn)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜,今天將帶大家一窺ios動(dòng)畫全貌。在這里你可以看...
    每天刷兩次牙閱讀 8,694評(píng)論 6 30
  • 在iOS中隨處都可以看到絢麗的動(dòng)畫效果,實(shí)現(xiàn)這些動(dòng)畫的過(guò)程并不復(fù)雜,今天將帶大家一窺iOS動(dòng)畫全貌。在這里你可以看...
    F麥子閱讀 5,270評(píng)論 5 13
  • 書寫的很好,翻譯的也棒!感謝譯者,感謝感謝! iOS-Core-Animation-Advanced-Techni...
    錢噓噓閱讀 2,440評(píng)論 0 6
  • Core Animation Core Animation,中文翻譯為核心動(dòng)畫,它是一組非常強(qiáng)大的動(dòng)畫處理API,...
    45b645c5912e閱讀 3,158評(píng)論 0 21
  • 在iOS實(shí)際開(kāi)發(fā)中常用的動(dòng)畫無(wú)非是以下四種:UIView動(dòng)畫,核心動(dòng)畫,幀動(dòng)畫,自定義轉(zhuǎn)場(chǎng)動(dòng)畫。 1.UIView...
    請(qǐng)叫我周小帥閱讀 3,325評(píng)論 1 23

友情鏈接更多精彩內(nèi)容