日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

iOS动画-CAAnimation使用详解

發布時間:2024/3/12 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 iOS动画-CAAnimation使用详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

理解了隱式動畫后,顯式動畫就更加通俗易懂了。區別于隱式動畫的特點,顯式動畫就是需要我們明確指定類型、時間等參數來實現效果的動畫。除此之外,我們也可以創建非線性動畫,比如沿著任意一條曲線運動等;
我們平時最常用的也是顯式動畫,不僅系統為我們的視圖提供了UIViewAnimationWithBlock的動畫封裝,而且我們在熟悉了Core Animation的動畫屬性后也可以很方便的設置顯式動畫;

本篇主要內容:
1.iOS動畫的分類
2.CAMediaTiming協議
3.CAAnimation基類
4.CAPropertyAnimation基類
5.基礎動畫CABasicAnimation
6.關鍵幀動畫CAKeyframeAnimation
7.動畫組CAGroupAnimation
8.過渡動畫CATransition
9.委托模式下的動畫區分
10.虛擬屬性及其作用
11.動畫的取消

相關文章:
iOS動畫-CALayer寄宿圖與繪制原理
iOS動畫-CALayer布局屬性詳解
iOS動畫-CALayer隱式動畫原理與特性
iOS動畫-CAAnimation使用詳解

一、動畫的分類

1、實現動畫的方式

如果根據實現動畫時直接操作對象的類型,我們可以簡單的將動畫分為視圖和圖層兩種;但事實上,無論UIViewAnimaiton動畫還是UIViewAnimaitonWithBlock動畫都只是對UIView的關聯圖層CALayer動畫的進一步封裝。

實現動畫的方式.png

2.核心動畫Core Animation常用類的繼承關系

我們在使用Core Animation動畫之前,有必要對核心動畫常見的類和動畫屬性做一個基本了解;從繼承關系的圖示中,我們可以十分清晰的看出這些屬性設置設置因何而來,以及它們各自的聯系。

核心動畫類的繼承關系.jpg

動畫類動畫特性
CAMediaTiming協議;定義了一段動畫內用于控制時間的屬性的集合
CAAnimation抽象類;作為所有動畫類型父類,不可直接使用
CAPropertyAnimation抽象類;作為基礎動畫和幀動畫的父類,不可直接使用
CABasicAnimation基礎動畫;用于實現單一屬性變化的動畫
CAKeyFrameAnimation關鍵幀動畫;用于實現單一屬性連續變化的動畫
CAAnimaitionGroup組動畫;用于實現多屬性同時變化的動畫
CATrasition轉場過渡動畫;

二、CAMediaTiming協議

CAMediaTiming協議定義了一段動畫內用于控制時間的屬性的集合,CALayer和CAAnimation都實現了這個協議,所以時間可以被任意基于一個圖層或者一段動畫的類控制,有關CAMediaTimg協議具體的屬性如下:

屬性參數類型具體描述
beginTimeCFTimeInterval動畫開始之前的延遲時間,這里的延遲從動畫添加到可見圖層上那一刻開始測量;
(設置動畫beginTime為1,動畫將延時1秒后開始執行)
durationCFTimeInterval動畫持續時間;
(默認值為0,但是實際動畫默認持續時間為0.25秒)
speedfloat動畫執行的速度;
(默認值為0,減少它會減慢動畫的時間,增加它會加快速度)
(設置speed為2時,則動畫實際執行時間是duration的一半)
timeOffsetCFTimeInterval動畫時間偏移量;
(設置時長3秒動畫的timeOffset為1時,動畫會從1秒位置執到最后,再執行之前跳過的部分)
repeatCountfloat動畫重復次數;默認值是0,但是實際默認動畫執行1次;
(設置為INFINITY,則一直執行);
(duration是2,repeatCount設置為3.5,則完整動畫時長7秒)
repeatDurationCFTimeInterval動畫重復的時間,讓動畫重復執行一個指定的時間;
(設置為INFINITY,一直執行)
repeatCount和repeatDuration可能會相互沖突,所以你只需要對其中一個指定非零值,對兩個屬性都設置非0值的行為沒有被定義;
autoreversesBOOL動畫從初始值執行到最終值,是否會反向回到初始值;
(設置為YES,動畫完成后將以動畫的形式回到初始位置)
fillModeNSStrinng決定當前對象在非動畫時間端段的動畫屬性值,如動畫開始之前和動畫結束之后

1.fillMode詳細說明

試想這樣一個問題:在beginTime非0(即動畫未真正執行之前),以及removeOnCompletion被設置為NO的動畫結束時,我們會遇到這樣一個問題:被設置動畫的屬性應該是什么值?
一種可能是屬性與動畫沒被添加之前保持一致,還有一種可能是保持動畫開始之前那一幀或者動畫結束那一幀,這就是所謂的填充。
CAMediaTiming的fillMode用來控制填充效果,它是一個NSString類型,有四種常量可供使用:

fillMode類型參數類型具體描述
kCAFillModeRemoved (default)NSString默認值,動畫開始前和結束后,動畫對圖層都沒有影響,圖層依然保持初始值
kCAFillModeForwardsNSString動畫結束后,圖層一直保持動畫后的最終狀態
kCAFillModeBackwardsNSString動畫開始前,只要加入動畫就會處于動畫的初始狀態
kCAFillModeBothNSString綜合了kCAFillModeForwards與kCAFillModeBackwards特性;
(動畫加入圖層到真正執行動畫的時間段里,圖層保持動畫初始狀態;動畫結束之后保持動畫最終狀態)

特別注意:removedOnCompletion需要設置為NO,否則fillMode不起作用;

2.CAMediaTiming屬性應用總結

時間屬性的綜合應用.png

三、CAAnimation基類

CAAnimation作為所有動畫類型父類,是一個抽象類;我們不能直接使用CAAnimation類,而是使用它的子類;關于它的定義如下:

@interface CAAnimation : NSObject<NSSecureCoding, NSCopying, CAMediaTiming, CAAction>@property(nullable, strong) CAMediaTimingFunction *timingFunction; @property(nullable, strong) id <CAAnimationDelegate> delegate; @property(getter=isRemovedOnCompletion) BOOL removedOnCompletion;@end

可以看到,CAAnimation動畫基類遵循了CAMediaTiming協議,而且另外包含了三個常用的動畫屬性;下面是對這三個屬性的總結:

1.動畫緩沖屬性timingFunction

動畫實際上就是在一段時間內隨著某個特定速率執行變化的過程,現實中的任何物體都會在運動中經歷加速或者減速的過程,而不是速度驟變;因此,CoreAnimation也內嵌了一系列標準的緩沖函數來使動畫看起來更平滑自然,這就是我們要說到的動畫緩沖。
timingFunction屬性是CAMediaTimingFunction類的一個對象,用來控制圖層動畫變換的速度;使用它需要調用+functionWithName:的構造方法,下面是可傳入的變量的介紹:

變量名具體說明
KCAMediaTimingFuncationLinear默認,勻速執行動畫
KCAMediaTimingFuncationEaseIn先慢慢加速,后突然停止
KCAMediaTimingFuncationEaseOut先全速開始,再慢慢減速停止
KCAMediaTimingFuncationEaseInEaseOut先慢慢加速,再慢慢減速
KCAMediaTimingFuncationDefault效果同KCAMediaTimingFuncationEaseInEaseOut

這五種不同的緩沖效果如下:

動畫緩沖屬性timingFunction.jpg

通過這種方法控制動畫速度,其實是使用不同的變量創建了不同的計時函數。比如KCAMediaTimingFuncationLinear選項創建的是一個線性的計時函數,這也是CAAnimation的timingFunction屬性為空時候的默認函數。

注意:KCAMediaTimingFuncationDefault相比KCAMediaTimingFuncationEaseInEaseOut的加速和減速過程稍微有些慢,兩者區別很難察覺;可能蘋果也覺得它更適合用于隱式動畫,就作為了隱式動畫的默認效果;但是創建顯式的CAAnimation時,KCAMediaTimingFuncationLinear才是默認效果而非KCAMediaTimingFuncationDefault;

UIKit動畫其實也同樣支持這些緩沖效果的使用,在我們使用UIViewAnimationBlock實現動畫的時候,可以給options參數提供了如下的常量來修改緩沖效果:

變量名具體說明
UIViewAnimationOptionCurveLinear默認,勻速執行動畫
UIViewAnimationOptionCurveEaseIn先慢慢加速,后突然停止
UIViewAnimationOptionCurveEaseOut先全速開始,再慢慢減速停止
UIViewAnimationOptionCurveEaseInOut先慢慢加速,再慢慢減速

2.動畫代理屬性delegate

/* Delegate methods for CAAnimation. */ @protocol CAAnimationDelegate <NSObject>@optional //動畫開始時調用 - (void)animationDidStart:(CAAnimation *)anim; //動畫結束時調用 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag; @end

3.removedOnCompletion

removedOnCompletion屬性默認為YES,表示動畫完成后就會從圖層上移除,圖層也會恢復到動畫執行前的狀態;當其修改為NO時,那么圖層將會保持動畫結束后的狀態,此時的fillMode屬性也將生效;

另外,removedOnCompletion設置為NO時,直到我們手動移除動畫,否則動畫將不會自動釋放;所以通常我們此時會給動畫添加一個非空的鍵,這樣可以在不需要動畫的時候把它從圖層上移除;

當我們需要防止動畫結束后回到初始狀態時,

只需設置removedOnCompletion、fillMode兩個屬性就可以了。

transformAnima.removedOnCompletion = NO; transformAnima.fillMode = kCAFillModeForwards;

解釋:為什么動畫結束后返回原狀態?
首先我們需要搞明白一點的是,layer動畫運行的過程是怎樣的?其實在我們給一個視圖添加layer動畫時,真正移動并不是我們的視圖本身,而是 presentation layer 的一個緩存。動畫開始時 presentation layer開始移動,原始layer隱藏,動畫結束時,presentation layer從屏幕上移除,原始layer顯示。這就解釋了為什么我們的視圖在動畫結束后又回到了原來的狀態,因為它根本就沒動過。

這個同樣也可以解釋為什么在動畫移動過程中,我們為何不能對其進行任何操作。我們點擊動畫不能響應事件,因為移動的只是layer, view還是在原始的位置.

所以在我們完成layer動畫之后,最好將我們的layer屬性設置為我們最終狀態的屬性,然后將presentation layer 移除掉。

四、CAPropertyAnimation基類

CAPropertyAnimation是一個抽象類,不能直接用于實現CALayer動畫操作,但是它的類定義中增加用于設置CALayer可被實現動畫的屬性keyPath,總結這些屬性如下:

屬性解讀
transform.rotation默認圍繞z軸旋轉,相當于transform.rotation.z
transform.rotation.x
transform.rotation.y
transform.rotation.z
分別圍繞x軸、y軸、z軸旋轉;
transform.scale在所有方向上進行縮放
transform.scale.x
transform.scale.y
transform.scale.z
分別在x軸、y軸、z軸方向上縮放;
transform.translation平移到指定坐標點
transform.translation.x
transform.translation.y
transform.translation.z
分別在x軸、y軸、z軸方向上平移;
zPositionz軸位置
opacity透明度
backgroundColor背景顏色
cornerRadius圓角大小
borderWidth邊框寬度
bounds圖層大小
contents寄宿圖內容
contentsRect可視內容
position圖層位置,類似transform.translation
shadowColor陰影顏色
shadowOffset陰影偏移
shadowOpacity陰影透明度
shadowRadius陰影角度

附:KeyPath官方參考鏈接

五、基礎動畫CABasicAnimation

CABasicAnimation即基礎動畫,在指定可動畫屬性后,動畫會按照預定的參數持續一定時間由初始值變換為終點值。其實,CABasicAnimation就相當于只有開始和結束兩個幀的特殊關鍵幀動畫(后續會詳解);

1.屬性說明

屬性屬性說明
fromValue起始值
toValue結束值
byValuekeyPath屬性的變化值

2.動畫演示

下面的示例使用CABasicAnimation實現了修改顏色圖層colorLayer的背景色為隨機顏色的動畫,具體的代碼如下:

@interface TestBacicAnimation1VC ()<CAAnimationDelegate> @property (nonatomic,strong) CALayer *colorLayer; @end@implementation TestBacicAnimation1VC- (void)viewDidLoad {[super viewDidLoad];//創建顯示顏色的圖層,添加于視圖控制器的View上CALayer *colorLayer = [CALayer layer];colorLayer.frame = CGRectMake(50, 50, 100, 100);colorLayer.backgroundColor = [UIColor redColor].CGColor;self.colorLayer = colorLayer;[self.view.layer addSublayer:colorLayer]; }- (IBAction)changeColor:(UIButton *)sender{//步驟1:創建動畫CABasicAnimation *animation = [CABasicAnimation animation];animation.keyPath = @"backgroundColor";//步驟2:設定動畫屬性animation.autoreverses = NO;animation.duration = 0.25;animation.repeatCount = 1;animation.removedOnCompletion = NO;animation.fillMode = kCAFillModeForwards;animation.delegate = self;UIColor *randomColor = [UIColor randomColor]; //自定義獲取隨機色的方法animation.toValue = (__bridge id _Nullable)(randomColor.CGColor);//步驟3:添加動畫到圖層[self.colorLayer addAnimation:animation forKey:@"keyPath_backgroundColor"]; }- (void)animationDidStop:(CABasicAnimation *)anim finished:(BOOL)flag{//禁用隱式動畫[CATransaction begin];[CATransaction setDisableActions:true];self.colorLayer.backgroundColor = (__bridge CGColorRef)anim.toValue;[CATransaction commit]; }

效果圖如下:

CABasicAnimation.gif

總結創建動畫的兩種方式如下:

//方法1:實例化同時指定動畫類型 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];//方法2:先實例化,再指定動畫類型 CABasicAnimation *animation = [CABasicAnimation animation]; animation.keyPath = @"backgroundColor";

3.關閉隱式動畫

對獨立圖層(即非UIView的關聯圖層,類似上述例子中的colorLayer)做更新屬性的顯式動畫,我們需要設置一個事務來禁用圖層行為,否則動畫會發生兩次,一次是因為顯式的CABasicAnimation,另一次是因為隱式動畫,從而導致我們看到的動畫異常。

補充--彈簧動畫使用

iOS 彈簧動畫詳解CASpringAnimation
?

六、關鍵幀動畫CAKeyframeAnimation

CACAKeyfameAnimation是CAPropertyAnimation的另一個子類,它和和CABasicAnimation一樣都只能作用于圖層對象的單一屬性;它們的區別在于:CACAKeyfameAnimation不限制于設置一個起始值和結束值,而是可以根據一連串的值來做動畫。其實,CABasicAnimation可看做是只有2個關鍵幀的CAKeyframeAnimation。

1.關鍵幀動畫常用屬性總結

關鍵幀動畫相對于基礎動畫的具有一些獨特的屬性,我們現將其總結如下:

屬性具體描述
values用于提供關鍵幀數據的數組,數組中每一個值都對應一個關鍵幀屬性值;
數組中的數據類型根據動畫類型(KeyPath)而不同;
當使用path的時候,values的值將會被自動忽略;
path用于提供關鍵幀數據的路徑;
path與values屬性作用相同,但是兩者互斥,同時指定values和path,path會覆蓋values的效果;
keyTimesktyTimes與Values中的值具有一一對應的關系,用于指定關鍵幀在動畫的時間點,取值范圍是[0,1];
若沒有設置keyTimes,則每個關鍵幀的時間是平分動畫總時長(duration);
timingFunctions用于指定每個關鍵幀之間的動畫緩沖效果,這類似于物體運動的加速度;
注意:存在幾個子路徑就應該在此數組中傳入幾個元素;
calculationMode該屬性決定了物體在每個子路徑下是跳著走還是勻速走,跟timeFunctions屬性有點類似;
rotationMode設置幀動畫是否需要按照路徑切線的方向運動;

2.實現幀動畫:使用values

從關鍵幀動畫的屬性可以看出,我們可以總結出關鍵幀動畫的實現方式實際分為兩種:
1.通過values設置關鍵幀屬性值數組;
2.通過path設置關鍵幀路徑,而且此種方式的優先級較高;
這里首先測試第一種方式,實現這樣的關鍵幀動畫:創建一個紫色滑塊在四個坐標點之間滑動;具體的代碼實現如下:

- (void)viewDidLoad {[super viewDidLoad];//創建測試幀動畫的紫色圖層UIView *purpleView = [UIView new];purpleView.frame = CGRectMake(0, 0, 50, 50);purpleView.center = CGPointMake(50, 100);purpleView.backgroundColor = [UIColor purpleColor];[self.view addSubview:purpleView];//步驟1:創建動畫CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];animation.keyPath = @"position";//步驟2:設置動畫關鍵幀數據NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(50, 100)];NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(kDeviceWidth -50, 100)];NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(kDeviceWidth -50, kDeviceWidth- 100)];NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(50, kDeviceWidth -100)];NSValue *value5 = [NSValue valueWithCGPoint:CGPointMake(50, 100)];animation.values = @[value1,value2,value3,value4,value5];//步驟3:設定動畫屬性animation.repeatCount = MAXFLOAT; //重復執行animation.autoreverses = NO;animation.removedOnCompletion = NO;animation.duration = 4;//animation.keyTimes = @[@(0), @(1 / 10.0), @(5 / 10.0), @(9 / 10.0), @(1) ];animation.timingFunctions = @[[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear],[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut],[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];[purpleView.layer addAnimation:animation forKey:nil]; }- (void)viewDidLoad {[super viewDidLoad];UIView *red = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 50, 50)];red.backgroundColor = [UIColor redColor];[self.view addSubview:red];// 也可以使用UIView的動畫來做[UIView animateKeyframesWithDuration:2 delay:0 options:UIViewKeyframeAnimationOptionCalculationModeLinear animations:^{[UIView addKeyframeWithRelativeStartTime:0 relativeDuration:0.25 animations:^{red.frame = CGRectMake(200, 100, 50, 50);}];[UIView addKeyframeWithRelativeStartTime:0.25 relativeDuration:0.5 animations:^{red.frame = CGRectMake(200, 200, 50, 50);}];[UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.75 animations:^{red.frame = CGRectMake(100, 200, 50, 50);}];[UIView addKeyframeWithRelativeStartTime:0.75 relativeDuration:1 animations:^{red.frame = CGRectMake(100, 100, 50, 50);}];} completion:^(BOOL finished) {}];}

關鍵幀動畫效果如下:

CAKeyframeAnimation_values.gif

3.實現關鍵幀動畫:使用path

現在,我們測試CAKeyframeAnimation使用path實現這樣一個動畫:一架飛機沿著一個簡單的曲線運動飛行;具體的操作包括以下幾個步驟:
1.使用UIKit提供的UIBezierPath類創建貝塞爾曲線,作為飛機飛行的路線軌跡;
2.使用CAShapeLayer在屏幕上繪制曲線(此步驟對于動畫不是必須的,只是為了動畫看起來更直觀);
3.創建用于顯示飛機的視圖,將其設置在貝塞爾曲線的初始位置;
4.創建并執行關鍵幀動畫,實現飛機飛行的曲線動畫;

- (void)viewDidLoad {[super viewDidLoad];//1.創建三次貝塞爾曲線(一種使用起始點,結束點和另外兩個控制點定義的曲線);UIBezierPath *bezierPath = [[UIBezierPath alloc] init];[bezierPath moveToPoint:CGPointMake(50, 200)];[bezierPath addCurveToPoint:CGPointMake(kDeviceWidth - 50, 200) controlPoint1:CGPointMake(150, 50) controlPoint2:CGPointMake(kDeviceWidth - 150, 250)];//2.繪制飛行路線CAShapeLayer *pathLayer = [CAShapeLayer layer];pathLayer.path = bezierPath.CGPath;pathLayer.fillColor = [UIColor clearColor].CGColor;pathLayer.strokeColor = [UIColor redColor].CGColor;pathLayer.lineWidth = 3.0f;[self.view.layer addSublayer:pathLayer];//3.創建顯示飛機的視圖UIImageView *airPlaneImgView = [[UIImageView alloc] init];airPlaneImgView.frame = CGRectMake(0, 0, 50, 50);airPlaneImgView.center = CGPointMake(50, 200);airPlaneImgView.image = [UIImage imageNamed:@"airplane"];[self.view addSubview:airPlaneImgView];//4.設置關鍵幀動畫CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];animation.keyPath = @"position";animation.duration = 5.0;animation.path = bezierPath.CGPath;animation.rotationMode = kCAAnimationRotateAuto; //設置根據曲線的切線自動旋轉,讓動畫更加真實[airPlaneImgView.layer addAnimation:animation forKey:nil]; }

關鍵幀動畫效果圖如下:

CAKeyframeAnimation_path.gif

七、動畫組CAGroupAnimation

CAGroupAnimation顧名思義,就是可以將不同的動畫效果組合起來,CABasicAnimation和CAKeyframeAnimation都僅僅作用于單一的屬性,而CAAnimationGrop可以設置其animations數組的屬性來組合別的動畫,從而達到混合多種動畫效果的目的;

下面演示一個動畫組的示例:組合基礎動畫和關鍵幀動畫,實現一個滑塊在沿path運動過程修改其顏色,具體的測試代碼如下:

@interface TestAnimationGroupVC ()@property (nonatomic,strong) UIView *colorView; @property (nonatomic,strong) UIBezierPath *bezierPath;@end@implementation TestAnimationGroupVC- (void)viewDidLoad {[super viewDidLoad];//創建顯示顏色的圖層self.colorView = [UIView new];self.colorView.frame = CGRectMake(0, 0, 60, 60);self.colorView.center = CGPointMake(50, 200);self.colorView.backgroundColor = [UIColor orangeColor];[self.view addSubview:self.colorView];//創建貝塞爾曲線,即幀動畫運動軌跡self.bezierPath = [[UIBezierPath alloc] init];[self.bezierPath moveToPoint:CGPointMake(50, 200)];[self.bezierPath addCurveToPoint:CGPointMake(kDeviceWidth - 50, 200) controlPoint1:CGPointMake(150, 50) controlPoint2:CGPointMake(kDeviceWidth - 150, 250)];//繪制繪制path,便于觀察動畫;CAShapeLayer *pathLayer = [CAShapeLayer layer];pathLayer.path = self.bezierPath.CGPath;pathLayer.fillColor = [UIColor clearColor].CGColor;pathLayer.strokeColor = [UIColor redColor].CGColor;pathLayer.lineWidth = 3.0f;[self.view.layer addSublayer:pathLayer]; }- (IBAction)startAnimation:(UIButton *)sender{ //移除可能未執行完的動畫,防止多重動畫導致異常[self.colorView.layer removeAnimationForKey:@"groupAnimation"];//1.創建基礎動畫:修改背景色為紫色CABasicAnimation *basicAnimation = [CABasicAnimation animation];basicAnimation.keyPath = @"backgroundColor";basicAnimation.toValue = (__bridge id _Nullable)([UIColor purpleColor].CGColor);//2.創建關鍵幀動畫CAKeyframeAnimation *keyFrameAnimation = [CAKeyframeAnimation animation];keyFrameAnimation.keyPath = @"position";keyFrameAnimation.path = self.bezierPath.CGPath;keyFrameAnimation.rotationMode = kCAAnimationRotateAuto;//3.創建組動畫:組合基礎動畫和關鍵幀動畫CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];groupAnimation.animations = @[basicAnimation, keyFrameAnimation];groupAnimation.duration = 4.0;[self.colorView.layer addAnimation:groupAnimation forKey:@"groupAnimation"]; }

動畫組的效果如下:

CAGroupAnimation.gif

八、過渡動畫CATransition

1.過渡動畫簡介

屬性動畫只能對圖層的可動畫屬性起作用,而過渡動畫可以改變非動畫屬性(比如交換一段文本和圖片),或者從層級關系中添加或者移除圖層;于是就有了過渡的概念;

過渡動畫使用CATransition來實現,它同樣是CAAnimation的子類;它并不像屬性動畫那樣在平滑的兩個值之間做動畫,而是影響到整個圖層的變化。過渡動畫首先展示之前的圖層外觀,然后通過一個交換過渡到新的外觀。

過渡動畫通常用于刪除子控件、添加子控件、切換兩個子控件等。

2.過渡動畫屬性介紹

過渡動畫有type和subtype兩個關鍵屬性,type用于指定動畫類型,subtype用于指定動畫移動的方向;
type屬性:
type屬性是一個NSString類型,用于控制整體動畫效果類型,具體的可選類型如下:

type值動畫效果對應常量是否支持方向
fade默認效果,漸變kCATransitionFade
moveIn覆蓋kCATransitionMoveIn
Push退出kCATransitionPush
Reveal揭開kCATransitionReveal
cube立方體無(私有類型)
suckEffect收縮無(私有類型)
oglFlip翻轉無(私有類型)
rippleEffect水波動畫無(私有類型)
pageCurl頁面揭開無(私有類型)只支持左右方向
vpageUnCurl放下頁面無(私有類型)只支持左右方向
cameraIrisHollowOpen鏡頭打開無(私有類型)
cameraIrisHollowClose鏡頭關閉無(私有類型)

目前為止,我們只能使用type的前四種公開屬性,但是我們可以通過一些別的方法來自定義過渡效果(后續介紹);

subtype屬性:
subtype屬性也是一個NSString類型,用于控制動畫方向,具體的可選類型如下:

Subtype類型具體描述
kCATransitionFromRight從右向左
kCATransitionFromLeft從左向右
kCATransitionFromTop從上向下
kCATransitionFromBottom從下向上

3.過渡動畫的使用

現在設想這樣的一個需求:修改UIImageView的image屬性,實現淡入淡出的平滑動畫的效果;此時我們需要使用CATransition來對非動畫屬性做動畫,具體的關鍵代碼如下:

@interface TestTransition1VC ()@property (nonatomic,strong) UIImageView *imageView; @property (nonatomic,strong) NSArray *images;@property (nonatomic, copy) NSString *type; @property (nonatomic, copy) NSString *subtype;@end@implementation TestTransition1VC- (void)viewDidLoad {[super viewDidLoad];self.images = @[[UIImage imageNamed:@"tree_spring"],[UIImage imageNamed:@"tree_summer"],[UIImage imageNamed:@"tree_autumn"],[UIImage imageNamed:@"tree_winter"]];self.type = kCATransitionFade;self.subtype = kCATransitionFromRight; }- (void)perforomTransitionAnimation{CATransition *transition = [[CATransition alloc] init];transition.type = _type;transition.subtype = _subtype;transition.duration = 0.5;[self.imageView.layer addAnimation:transition forKey:nil];UIImage *currentImage = self.imageView.image;NSUInteger index = [self.images indexOfObject:currentImage];index = (index + 1) % self.images.count;self.imageView.image = self.images[index]; }

過渡動畫的效果如下:

CATransition.gif

注意:和屬性動畫不同,對指定圖層一次只能使用那一次CATransition,因此無論對動畫的鍵設置為什么值,過渡動畫都會對它的鍵設置為”transition”,也就是常量KCATransition.

4.隱式過渡

CATransition可以對圖層任何變化平滑過渡,這使得它成為那些不好做動畫的屬性圖層行為的理想之選。所以,蘋果將CATransition作為設置CALayer的contents屬性時的默認行為,對圖層contents圖片做的改動都會自動附上淡入淡出的效果,這也就解釋了隱式動畫的原理;
但注意:
1.對于視圖關聯的圖層,過渡動畫的默認效果是禁用的;
2.我們不能錯誤的理解CATransition只可以改變非動畫屬性,其實它也可以對類似backgroundColor的屬性做過渡效果動畫;

5.自定義過渡動畫

過渡動畫的過程就是對原始圖層外觀截圖,然后添加一段動畫,平滑過渡到圖層改變之后的那個截圖效果。如果我們知道如何對圖層截圖,我們就可以使用屬性動畫來自定義CATransition動畫了。

CALayer有一個-renderInContenxt:方法,通過它可以將圖層繪制到Core Graphics的上下文中捕獲當前內容的圖片;所以現在我們嘗試這樣的實現:對當前視圖控制器View進行截圖,然后在改變其背景色的時候對截圖快速旋轉并且淡出,以達到一種過渡的效果;具體的代碼示例如下:

- (void)performAnimation{UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, YES, 0.0);[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];UIImage *coverImage = UIGraphicsGetImageFromCurrentImageContext();UIView *coverView = [[UIImageView alloc] initWithImage:coverImage];coverView.frame = self.view.bounds;[self.view addSubview:coverView];//使用自定義方法得到隨機顏色(切換后的顏色)UIColor *randomColor = [UIColor randomColor];self.view.backgroundColor = randomColor;//使用UIView動畫方法來代替屬性動畫(為了簡化代碼步驟)[UIView animateWithDuration:1 animations:^{CGAffineTransform transform = CGAffineTransformMakeScale(0.01, 0.01);transform = CGAffineTransformRotate(transform, M_PI_2);coverView.transform = transform;coverView.alpha = 0.0;} completion:^(BOOL finished) {[coverView removeFromSuperview];}];}

自定義過渡動畫的效果如下:

CATransitionn_Custom.gif

注意:-renderInContext:捕獲了圖層的圖片和子圖層,但是不能對子圖層正確的處理變換效果,而且對視頻和OpenGL內容也不起作用。但是使用CATransition,或者使用私有的截屏方式就沒有這個限制了。

九、委托模式下的動畫區分

對于CAAnimation而言,使用委托模式而不是一個完成塊會帶來一個問題,那就是設置多個動畫時,無法在回調方法中區分。通常視圖控制器本身會作為一個委托,但所有動畫都會調用同一個回調方法,所以我們需要判斷到底是哪個圖層的動畫調用;

首先,動畫本身會作為一個參數傳入委托的方法,也許你會認為可以在控制器中把動畫存儲為一個屬性,然后在回調用比較,但實際上并不起作用,因為委托傳入的動畫參數是原始值的一個深拷貝,從而不是同一個值。最后,這里提供兩種思路來解決這個問題:
思路1:唯一key參數
當使用-addAnimation:forkey:添加動畫到圖層時,對每個動畫都關聯一個唯一的鍵,這樣就可以對每個圖層循環所有鍵,然后調用animationForKey:來對比結果;

思路2:KVC(鍵-值-編碼)協議
像所有NSObject子類一樣,CAAnimation也遵循了KVC協議,就像一個NSDictionary一樣允許我們隨意設置鍵值對;于是我們可以使用setValue:forKey:和-valueForKey:來存取屬性,通過為對象創建一個鍵值對來判斷區分動畫;

驗證上述兩種思路的具體的代碼使用如下:

@interface TestBacicAnimation2VC ()<CAAnimationDelegate>@property (nonatomic,strong) UIView *colorView; @property (nonatomic,strong) UIView *opacityView;@end@implementation TestBacicAnimation2VC#pragma mark - Life Cycle- (void)viewDidLoad {[super viewDidLoad];//創建顯示顏色的圖層UIView *colorView = [UIView new];colorView.frame = CGRectMake(50, 50, 100, 100);colorView.backgroundColor = [UIColor redColor];self.colorView = colorView;[self.view addSubview:self.colorView];//創建透明度視圖UIView *opacityView = [UIView new];opacityView.frame = CGRectMake(50, 200, 100, 100);opacityView.backgroundColor = [UIColor blueColor];self.opacityView = opacityView;[self.view addSubview:self.opacityView]; }- (IBAction)startAnimation:(UIButton *)sender{ //背景色顏色動畫CABasicAnimation *animation1 = [CABasicAnimation animation];animation1.keyPath = @"backgroundColor";animation1.autoreverses = NO;animation1.duration = 1;animation1.repeatCount = 1;animation1.removedOnCompletion = NO;animation1.fillMode = kCAFillModeForwards;animation1.delegate = self;UIColor *randomColor = [UIColor randomColor]; //自定義獲取隨機色的方法animation1.toValue = (__bridge id _Nullable)(randomColor.CGColor);[animation1 setValue:@"animation_background" forKey:@"AnimationKey"];[self.colorView.layer addAnimation:animation1 forKey:@"key_backgroundColor"];//透明度動畫CABasicAnimation *animation2 = [CABasicAnimation animation];animation2.keyPath = @"opacity";animation2.autoreverses = NO;animation2.duration = 5;animation2.repeatCount = 1;animation2.removedOnCompletion = NO;animation2.fillMode = kCAFillModeForwards;animation2.delegate = self;animation2.fromValue = @(1);animation2.toValue = @(0);[animation2 setValue:@"animation_opacity" forKey:@"AnimationKey"];[self.opacityView.layer addAnimation:animation2 forKey:@"key_opacity"]; }//動畫結束的代理:區分動畫- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{//方法1:唯一key參數if([[self.colorView.layer animationForKey:@"key_backgroundColor"] isEqual:anim]){}if([[self.opacityView.layer animationForKey:@"key_opacity"] isEqual:anim]){}//方法2:KVCNSString *animationValue = [anim valueForKey:@"AnimationKey"];NSLog(@"animationValue:%@",animationValue);if([animationValue isEqualToString:@"animation_background"]){}else if([animationValue isEqualToString:@"animation_opacity"]){}}

注意:使用唯一key參數這種方法,必須設置removeOnCompletion為NO,否則通過animaitonForKey:獲取的CAAnimation對象為空對象無法進行比較。

十、虛擬屬性

屬性動畫CAPropertyAnimation的keyPath實際上針對的是關鍵路徑而不是一個鍵,這就意味著屬性動畫作用的對象可以子屬性(即屬性的屬性)甚至虛擬屬性;

那么什么是虛擬屬性呢?舉個例子來講,CATransform3D實際上是一個結構體而非一個對象,所以它并不符合KVC相關屬性,但是我們卻可以使用transform.rotation來實現動畫;這其實就是因為transform.rotation是一個CALayer可用于處理動畫變換的虛擬屬性;

1.虛擬屬性的作用

為了理解虛擬屬性的用處,我們現在考慮這樣一個動畫:對一個物體實現旋轉動畫,由于CALayer并沒有顯式的給提供角度或者方向之類的屬性,所以我們自然想到使用transform屬性來實現動畫,測試代碼具體如下:

@interface TestBacicAnimation3VC ()@property(nonatomic, strong) UILabel *txtLabel;@end@implementation TestBacicAnimation3VC- (void)viewDidLoad {[super viewDidLoad];//創建測試虛擬屬性的Label_txtLabel = [UILabel new];_txtLabel.frame = CGRectMake(50, 300, kDeviceWidth -100 , 50);_txtLabel.backgroundColor = [UIColor purpleColor];_txtLabel.font = [UIFont boldSystemFontOfSize:15];_txtLabel.textAlignment = NSTextAlignmentCenter;_txtLabel.text = @"測試虛擬屬性";[self.view addSubview:_txtLabel]; }- (IBAction)startAnimation:(UIButton *)sender{//步驟1:創建動畫CABasicAnimation *animation = [CABasicAnimation animation];animation.keyPath = @"transform”; //代碼1//步驟2:設定動畫屬性animation.autoreverses = NO;animation.removedOnCompletion = NO;animation.fillMode = kCAFillModeForwards;animation.duration = 1;animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI * 2, 0, 0, 1)]; //代碼2[_txtLabel.layer addAnimation:animation forKey:nil];}

在此例中,我們把旋轉角度從M_PI(180度)調整到M_PI*2(360度),對比兩次動畫會發現,txtLabel完全看不到旋轉的動畫效果;這是因為CATransform3D矩陣做了360度旋轉其實適合0度是一樣的,所以最后的值根本就沒變;

這里就需要用到上述說到的虛擬屬性了,為了旋轉圖層,我們可以針對于transform.rotation關鍵路徑應用動畫,而不是transform本身;現在將對上述代碼進行修改如下:

//animation.keyPath = @"transform"; //代碼1 animation.keyPath = @"transform.rotation”;//animation.toValue = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI * 2, 0, 0, 1)]; //代碼2 animation.byValue = @(M_PI * 2);

再來看動畫的效果如下:

CABasicAnimation_VirtualProperty.gif

總結transform.rotation相比transfrom做動畫的好處如下:

  • 可以不通過關鍵幀,只一個步驟就實現旋轉多于180度的動畫;
  • 可以使用相對值而不是絕對值旋轉,設置byValue而不是toValue;
  • 可以不用創建CATransform3D,而是使用一個簡單的數值來指定角度;
  • 不會和transform.position或者transfrom.scale沖突(同樣是使用關鍵路徑來做獨立的動畫屬性);
  • 2.虛擬屬性原理

    我們已經說過CATransform3D是一個結構體而非一個對象,所以transfrom.rotation其實是不存在的,我們不可以直接設置transform.rotation或者transform.scale;

    實際上,Core Animation是自動通過CAValueFunction計算的值來更新transform屬性的,CAValueFunction將我們賦值虛擬屬性transfom.rotation的浮點值轉換成了真正能用于擺放圖層的CATransform3D矩陣值;我們也可以通過設置CAPropertyAnimation的valuefunction屬性來改變,這樣我們自定義函數就會覆蓋默認函數。

    CAValueFuncation對于那些不能簡單相加的屬性(例如變換矩陣)做動畫十分有用,但是此方法的實現細節是私有的,所以,目前我們并不能通過繼承來自定義此方法;我們可以通過使用蘋果已經提供的常量來改善動畫(目前都是和變換矩陣的虛擬屬性相關,所以沒太多的應用場景了,因為這些屬性都有了默認的實現方式)。

    十一、在動畫過程中取消動畫

    在使用動畫的過程中,我們可能需要適時的移除不要的動畫,否則就可能造成內存的泄漏問題;從圖層中取消動畫的方法有以下兩種方式:

    //方法1:取消指定動畫 /* Remove any animation attached to the layer for 'key'. */ - (void)removeAnimationForKey:(NSString *)key;//方法2:移除所有動畫 /* Remove all animations attached to the layer. */ - (void)removeAllAnimations;

    關于移除動畫的幾點說明:
    1.動畫一旦被移除,圖層的外觀就立刻更新到當前的模型圖層的值;
    2.動畫通常默認結束之后被自動移除,除非設置了removeCompletion為NO;
    3.動畫若設置為結束之后不自動移除,那么我們在不需要的時候需手動移除,否則它會一直在內存中,直到圖層被銷毀;

    - (void)viewDidLoad {[super viewDidLoad];//創建測試停止動畫的Label_txtLabel = [UILabel new];_txtLabel.frame = CGRectMake(50, 200, kDeviceWidth -100 , 50);_txtLabel.backgroundColor = [UIColor purpleColor];_txtLabel.font = [UIFont boldSystemFontOfSize:18];_txtLabel.textAlignment = NSTextAlignmentCenter;_txtLabel.textColor = [UIColor whiteColor];_txtLabel.text = @"測試停止動畫的Label";[self.view addSubview:_txtLabel];//添加開始動畫的按鈕[self.view addSubview:self.button];[self.button mas_makeConstraints:^(MASConstraintMaker *make) {make.bottom.equalTo(self.view).offset(-50);make.leading.equalTo(self.view).offset(60);make.trailing.equalTo(self.view).offset(-60);make.height.mas_equalTo(50);}]; }- (void)onBtnClick:(UIButton *)btn {btn.selected = !btn.selected;if (btn.selected) {//停止動畫[self.txtLabel.layer removeAnimationForKey:@"Animation_transform_rotation"];[self.button setTitle:@"開始動畫" forState:UIControlStateNormal];}else{//開始動畫CABasicAnimation *animation = [CABasicAnimation animation];animation.keyPath = @"transform.rotation";animation.delegate = self;animation.duration = 5;animation.byValue = @(M_PI * 2);[self.txtLabel.layer addAnimation:animation forKey:@"Animation_transform_rotation"];[self.button setTitle:@"停止動畫" forState:UIControlStateNormal];} } - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{NSLog(@"The animation stopped (finished:%@)",flag? @"YES" : @"NO"); }

    測試取消動畫效果圖如下:

    CAAnimation_cancel.gif

    代碼分析:
    -animationDidStop:finished:方法中的flag參數表明了動畫是自然結束還是被打斷的;此例中通過停止按鈕來終止動畫會打印NO,自然完成動畫時打印YES;

    總結

    以上是生活随笔為你收集整理的iOS动画-CAAnimation使用详解的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。