核心動畫的繼承關系圖
一、基礎動畫
改變位移、透明度、縮放、旋轉、背景色改變等行為產生的動畫,系統CALyer
的核心動畫的有些Api在改變指定屬性后默認就能產生動畫,只有非root layer才有隱式動畫,進入頭文件查看凡是有標注Animatable
的Api默認都可以產生動畫,例如下面的position
屬性:
下面分別使用三種方式實現位移動畫為樣例,這幾種雖然效果都是一樣的,但是使用不同的系統API會有不同的動畫屬性和狀態;其他的動畫用法都一樣,只需要修改keyPath
和fromValue
、toValue
的值即可。
- 實現方式一:使用
CAlayer
的核心動畫實現方式
//使用CABasicAnimation創建基礎動畫
-(void)testAnimation
{
CABasicAnimation *anima = [CABasicAnimation animation];
anima.keyPath = @"position"; //整體平移
//anima.keyPath = @"position.x"; //則x方向平移多少
//anima.keyPath = @"transform.translation; //也是平移動畫
//動畫從一個點執行到另一個點,記得這里接收的值要包裝成 `NSValue` 。。。
anima.fromValue = [NSValue valueWithCGPoint:CGPointMake(0, SCREEN_HEIGHT/2-75)];
anima.toValue = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-75)];
//在原來值的基礎上增加多少
//anima.byValue =@(80.0);
//動畫執行時間
anima.duration = 1.0f;
//設置動畫保持結束的狀態,注意:實際上圖層的屬性值還是動畫執行前的初始值,并沒有真正被改變。
anima.fillMode = kCAFillModeForwards;
anima.removedOnCompletion = NO;
//動畫執行次數,`MAXFLOAT`表示最大次數。
anima.repeatCount = MAXFLOAT;
//設置當前為實例為動畫的代理<CAAnimationDelegate>,可監聽動畫的開始和結束。
anima.delegate = self;
//控制動畫執行的速度節奏,具體有哪些值可進入頭文件查看。
anima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];
[_demoView.layer addAnimation:anima forKey:@"positionAnimation"];
}
1.1 CAPropertyAnimation
中KeyPath
的值可以有以下這些
transform.scale = 比例轉換
transform.rotation = 旋轉
opacity = 透明度
margin = 邊距
position = 位移
backgroundColor = 背景顏色
cornerRadius = 圓角
borderWidth = 邊框寬度
bounds = 位置,體積
contents = 內容
contentsRect = 面積
frame = 位置,體積
hidden = 是否隱藏
mask = 任務
masksToBounds
shadowColor = 陰影顏色
shadowOffset = 陰影偏移
shadowOpacity = 陰影透明
shadowRadius = 陰影半徑
1.2 這里的keyPath
能設置的值在系統API中的有這些,可以在Xcode的蘋果官方文檔中鍵入關鍵字CATransform3D value key paths
搜索能看到詳細的用法:
2 . 實現方式二: 使用UIView的Block動畫實現方式
//使用UIView Animation 代碼塊調用
-(void)testAnimation2
{
[UIView animateWithDuration:1.0f animations:^{
//平移動畫
_demoView.transform = CGAffineTransformMakeTranslation(SCREEN_WIDTH, SCREEN_HEIGHT/2-50);
} completion:^(BOOL finished) {
//復原transform值,復原也會使控件位置復原
_demoView.transform = CGAffineTransformIdentity;
}];
}
3 . 實現方式三: 使用UIVew的【begin commit 】這種用法在開發中使用較少
//使用UIView [begin,commit]模式
-(void)testAnimation3
{
_demoView.frame = CGRectMake(0, SCREEN_HEIGHT/2-50, 50, 50);
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:1.0f];
_demoView.frame = CGRectMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-50, 50, 50);
[UIView commitAnimations];
}
** 對比總結:** 這里三種方式都可以實現相同的動畫,不同的實現方式有不同的好處,在我們項目中可以根據自己的實際需求選擇相應的實現方式,建議是:在對UI控件的大小位置有沒有要求的情況下我們推薦使用CALayer
的核心動畫實現,沒有要求的可以使用UIView
的Block
方式實現;而核心動畫都是后臺執行的動畫它不會阻塞UI線程,能執行很多絢麗的組合動畫,但也有一些弊端,比如動畫在頁面之間切換、前后臺的切換后動畫有可能會停止,這些弊端在使用UIView
的Block
方式就不會有這樣的問題,相應的UIView
動畫能執行的動畫類型相對少很多。
二、幀動畫
關鍵幀動畫CAKeyframeAnimation
類繼承于CAPropertyAnimation
類,屬于屬性動畫的范疇,它能做一組相同的動畫,因此它有一個values
數組Api屬性,數組中都是每個關鍵幀所做的動畫值,常見的關鍵幀動畫比如做平移、圖標抖動等動畫。
1 . 一組移動位置的 關鍵幀動畫用法例子:
/**
* 關鍵幀動畫
*/
-(void)keyFrameAnimation{
CAKeyframeAnimation *anima = [CAKeyframeAnimation animation];
anima.keyPath = @"position"; //關鍵幀平移動畫
NSValue *value0 = [NSValue valueWithCGPoint:CGPointMake(0, SCREEN_HEIGHT/2-50)];
NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/3, SCREEN_HEIGHT/2-50)];
NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/3, SCREEN_HEIGHT/2+50)];
NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2+50)];
NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2-50)];
NSValue *value5 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-50)];
//每個動畫的值,需要包裝成`NSValue`類型
anima.values = [NSArray arrayWithObjects:value0,value1,value2,value3,value4,value5, nil];
anima.duration = 2.0f;
//設置動畫的節奏
anima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
//設置代理,可以檢測動畫的開始和結束
anima.delegate = self;
[_demoView.layer addAnimation:anima forKey:@"keyFrameAnimation"];
}
2 . 關鍵幀動畫CAKeyframeAnimation
類還有一個CGPathRef
類型的path
屬性,因此也可以做一些圍繞路徑的幀動畫,例如下面的path
轉圈動畫:
/**
* path動畫
*/
-(void)pathAnimation{
CAKeyframeAnimation *anima = [CAKeyframeAnimation animationWithKeyPath:@"position"];
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(SCREEN_WIDTH/2-100, SCREEN_HEIGHT/2-100, 200, 200)];
anima.path = path.CGPath;
anima.duration = 2.0f;
[_demoView.layer addAnimation:anima forKey:@"pathAnimation"];
}
三、轉場動畫
1 . 實現方式一:使用transitionFromView: toView: duration:...
此Api的轉過渡場動畫是發生在兩個View
之間, 官方文檔上明確提出: 在動畫完畢之后, fromView
會出從父視圖上被移除, 而 toView
會被添加到 fromView
所在的父視圖,因此在做系統的反轉動畫時,需要一個自定義的父視圖View
,如果直接使用控制器的View
,則會把當前控制器的View
一并反轉。 代碼例子如下:
- (void)transitionAnimation1
{
self.goingToFront = !self.goingToFront;
UIView *fromView = self.goingToFront ? self.imageView1 : self.imageView2;
UIView *toView = self.goingToFront ? self.imageView2 : self.imageView1;
UIViewAnimationOptions transitionDirection =
self.goingToFront ? UIViewAnimationOptionTransitionFlipFromRight : UIViewAnimationOptionTransitionFlipFromLeft;
[UIView transitionFromView:fromView
toView:toView
duration:0.5
options:transitionDirection
completion:^(BOOL finished) {
NSLog(@"動畫完成\n===%@\n===%@\n===%@",self.imageView1,self.imageView2,self.contentView);
}];
}
2 . 實現方式二:使用transitionWithView: duration:...
此Api這種類型的轉場動畫是發生在一個View
之間的過渡轉場,因為參數就只傳入一個UIView
. 代碼例子如下:
- (void) transitionAnimation2
{
self.goingToFront = !self.goingToFront;
UIViewAnimationOptions transitionDirection =
self.goingToFront ? UIViewAnimationOptionTransitionFlipFromRight : UIViewAnimationOptionTransitionFlipFromLeft;
[UIView transitionWithView:self.contentView
duration:0.5
options:transitionDirection
animations:^{
[self.contentView exchangeSubviewAtIndex:0 withSubviewAtIndex:1];
} completion:^(BOOL finished) {
NSLog(@"動畫完成");
}];
}
3 . 實現方式三:使用CATransition
此Api的核心動畫 . 代碼例子如下:
-(void) transitionAnimation3{
CATransition *anima = [CATransition animation];
//設置動畫的類型
anima.type = kCATransitionPush;
//設置動畫的方向
anima.subtype = kCATransitionFromRight;
//動畫持續時間
anima.duration = 1.0f;
//設置動畫起始點
anima.startProgress = 0.3;
//設置動畫結束點
anima.endProgress = 0.8;
[_demoView.layer addAnimation:anima forKey:@"pushAnimation"];
}
3.1 這里的anima.type
動畫的類型: 可選的動畫類型非常有限,系統默認提供了四個值:
kCATransitionFade; //逐漸消失
kCATransitionMoveIn; //逐漸進入
kCATransitionPush; //類似導航控制器的push動畫
kCATransitionReveal; //逐漸移出
3.2 除系統默認提供的動畫類型之外,還有一些私有Api的動畫類型,不過使用這些私有Api會有上架被拒風險,具體的類型如下:
Fade = 1, //淡入淡出
Push, //推擠
Reveal, //揭開
MoveIn, //覆蓋
Cube, //立方體
SuckEffect, //吮吸
OglFlip, //翻轉
RippleEffect, //波紋
PageCurl, //翻頁
PageUnCurl, //反翻頁
CameraIrisHollowOpen, //開鏡頭
CameraIrisHollowClose, //關鏡頭
CurlDown, //下翻頁
CurlUp, //上翻頁
FlipFromLeft, //左翻轉
FlipFromRight, //右翻轉
3.3 anima.subtype
動畫的方向:系統默認也提供了四個值:
kCATransitionFromRight; //從右邊開始
kCATransitionFromLeft; //從左邊開始
kCATransitionFromTop; //從上面開始
kCATransitionFromBottom; //從下面開始
四、組合動畫
CAAnimation
的子類,可以保存一組動畫對象,將CAAnimationGroup
對象加入層后,組中所有動畫對象可以同時并發運行。重要屬性:animations
用來保存一組動畫對象的NSArray
一個同時執行的組合動畫例子:
/**
* 組合動畫
*/
-(void)groupAnimation{
//位移動畫
CAKeyframeAnimation *anima1 = [CAKeyframeAnimation animationWithKeyPath:@"position"];
NSValue *value0 = [NSValue valueWithCGPoint:CGPointMake(0, SCREEN_HEIGHT/2-50)];
NSValue *value1 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/3, SCREEN_HEIGHT/2-50)];
NSValue *value2 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH/3, SCREEN_HEIGHT/2+50)];
NSValue *value3 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2+50)];
NSValue *value4 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH*2/3, SCREEN_HEIGHT/2-50)];
NSValue *value5 = [NSValue valueWithCGPoint:CGPointMake(SCREEN_WIDTH, SCREEN_HEIGHT/2-50)];
anima1.values = [NSArray arrayWithObjects:value0,value1,value2,value3,value4,value5, nil];
//縮放動畫
CABasicAnimation *anima2 = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
anima2.fromValue = [NSNumber numberWithFloat:0.8f];
anima2.toValue = [NSNumber numberWithFloat:2.0f];
//旋轉動畫
CABasicAnimation *anima3 = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
anima3.toValue = [NSNumber numberWithFloat:M_PI*4];
//組動畫
CAAnimationGroup *groupAnimation = [CAAnimationGroup animation];
groupAnimation.animations = [NSArray arrayWithObjects:anima1,anima2,anima3, nil];
groupAnimation.duration = 4.0f;
[_demoView.layer addAnimation:groupAnimation forKey:@"groupAnimation"];
}