CALayer
一、簡介
1.CALayer
簡單的說是層(圖層)的概念,類似與PS中的圖層。一個用來完成繪制、渲染、動畫等效果的可見的容器。
2.CALayer 與 UIView 的聯系與區別
UIView中有一個layer屬性,為根圖層。UIView之所以能顯示在屏幕上,完全是因為它內部的CALayer對象。在創建UIView對象時,UIView內部會自動創建一個層(即CALayer對象),通過UIView的layer屬性可以訪問這個層。當UIView需要顯示到屏幕上時,會調用drawRect:方法進行繪圖,并且會將所有內容繪制在自己的層上,繪圖完畢后,系統會將層拷貝到屏幕上,于是就完成了UIView的顯示。UIView本身不具備顯示的功能,顯示的是UIView的CALayer。
UIView可以通過subviews屬性訪問所有的子視圖,同樣地,CALayer也可以通過sublayers屬性訪問所有的子層;UIView可以通過superview屬性訪問父視圖,同樣地,CALayer也可以通過superlayer屬性訪問父層。
UIView可以通過addSubview:方法添加子視圖,同樣地,CALayer可以通過addSublayer:方法添加子層。
在使用時,往往CALayer和UIView都能實現相同的顯示效果,但是UIView具有交互的能力,可以處理事件。如果顯示出來的東西需要跟用戶進行交互的話,那么就要使用用UIView。
3.常用屬性
屬性 | 說明 | 是否支持隱式動畫 |
---|---|---|
anchorPoint | 和中心點position重合的一個點,稱為“錨點”,錨點的描述是相對于x、y位置比例而言的默認在圖像中心點(0.5,0.5)的位置 | 是 |
backgroundColor | 圖層背景顏色 | 是 |
borderColor | 邊框顏色 | 是 |
borderWidth | 邊框寬度 | 是 |
bounds | 圖層大小 | 是 |
contents | 圖層顯示的內容 | 是 |
contentsRect | 圖層顯示內容的大小和位置 | 是 |
cornerRadius | 圓角半徑 | 是 |
doubleSided | 圖層背面是否顯示,默認為YES | 否 |
frame | 圖層大小和位置 | 否 |
hidden | 是否隱藏 | 是 |
mask | 圖層蒙版 | 是 |
maskToBounds | 子圖層是否剪切圖層邊界,默認為NO | 是 |
opacity | 透明度 | 是 |
position | 圖層中心點位置 | 是 |
shadowColor | 陰影顏色 | 是 |
shadowOffset | 陰影偏移量 | 是 |
shadowOpacity | 陰影透明度,默認為0,設置陰影必須設置此屬性 | 是 |
shadowPath | 陰影的形狀 | 是 |
shadowRadius | 陰影模糊半徑 | 是 |
sublayers | 子圖層 | 是 |
sublayerTransform | 子圖層形變 | 是 |
transform | 圖層形變 | 是 |
注:
- 在CALayer中很少使用frame屬性,因為frame本身不支持隱式動畫,通常使用bounds和position代替
- anchorPoint屬性是圖層的錨點,范圍在(01,01)表示在x、y軸的比例,這個點決定著CALayer身上的哪個點會與position所指定的位置重合,當圖層中心點固定后,調整anchorPoint即可達到調整圖層顯示位置的作用(因為它永遠和position重合)
4.隱式動畫屬性
每一個UIView內部都默認關聯著一個CALayer,這個CALayer為Root Layer(根層)。對于所有的非Root Layer,即手動創建的CALayer對象,當對非Root Layer的部分屬性進行相應的修改時,默認會自動產生一些動畫效果,這些屬性稱為Animatable Properties(可動畫屬性)。隱式屬性動畫的本質是這些屬性的變動默認隱含了CABasicAnimation動畫實現,例如修改bounds屬性會產生縮放動畫,修改backgroundColor屬性會產生背景色的漸變動畫,修改position屬性會產生平移動畫。
可以通過動畫事務(CATransaction)關閉默認的隱式動畫效果
[CATransactionbegin];
[CATransactionsetDisableActions:YES];
self.myview.layer.position= CGPointMake(100, 100);
[CATransactioncommit];
二、簡單使用
1. 設置陰影
imageView.layer.shadowColor = [UIColor grayColor].CGColor; //陰影顏色
imageView.layer.shadowOffset = CGSizeMake(10, 10); //陰影偏移量
imageView.layer.shadowOpacity = 0.5; //陰影透明度
2.設置圓角
imageView.layer.masksToBounds = YES;
imageView.layer.cornerRadius = 10; //當cornerRadius等于邊長的一半時,就是圓
3.設置邊框
imageView.layer.borderWidth = 5;
imageView.layer.borderColor = [UIColor redColor].CGColor;
4.設置旋轉
imageView.layer.transform = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);// 順時針旋轉45
5.繪制圖層
繪制圖層有兩種方法
1.通過圖層代理drawLayer: inContext:方法繪制
2.通過自定義圖層drawInContext:方法繪制
不管使用哪種方法繪制完必須調用圖層的setNeedDisplay方法.
① 使用代理方法繪制圖層
通過代理方法進行圖層繪圖只要指定圖層的代理,然后在代理對象中重寫-(void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx方法即可。需要注意這個方法雖然是代理方法但是不用手動實現CALayerDelegate,因為CALayer定義中給NSObject做了分類擴展,所有的NSObject都包含這個方法。另外設置完代理后必須要調用圖層的setNeedDisplay方法,否則繪制的內容無法顯示。
例
- (void)viewDidLoad {
[super viewDidLoad];
CALayer * layer = [[CALayer alloc]init];
layer.bounds = CGRectMake(0, 0, 100, 100);
layer.position = CGPointMake(50, 50);
layer.masksToBounds = YES;
layer.cornerRadius = 50;
layer.borderWidth = 2;
layer.borderColor = [UIColor yellowColor].CGColor;
layer.delegate = self; //設置代理
[self.view.layer addSublayer:layer]; //添加圖層
[layer setNeedsDisplay]; //重繪
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx{
CGContextSaveGState(ctx);
CGContextScaleCTM(ctx, 1, -1); //轉換坐標系
CGContextTranslateCTM(ctx, 0, -100);
UIImage * image = [UIImage imageNamed:@"me"];
CGContextDrawImage(ctx, CGRectMake(0, 0, 100, 100), image.CGImage);
CGContextRestoreGState(ctx);
}
② 使用自定義圖層繪制圖層
創建一個CALayer的子類,然后重寫drawInContext:方法,在方法中使用Quartz2D API進行繪圖;
同樣,使用時,必須要調用圖層的setNeedDisplay方法,才會觸發drawInContext:方法的調用。
Core Animation
一、簡介
1.概念
Core Animation 是作用在CALayer上的,使用Core Animation 創建動畫不僅簡單,而且還有更好的性能,因為Core Animation 是在單獨的線程中完成的,不會阻塞主線程,而且只會重繪界面上變化的部分(局部刷新)。
2.分類
類別 | 說明 |
---|---|
CAAnimation | 所有動畫類的基類,不能直接使用,實現了CAMediaTiming協議,提供了動畫的持續時間、速度和重復計數等,還實現了CAAction協議,該協議為CALayer動畫觸發的動作提供標準化響應。 |
CATransition | CAAnimation的子類,轉場動畫,能夠為層提供移出屏幕和移入屏幕的動畫效果 |
CAAnimationGroup | CAAnimation的子類,動畫組,是一種組合模式設計,可以通過動畫組來進行所有動畫行為的統一控制,組中所有動畫效果可以并發執行。 |
CAPropertyAnimation | CAAnimation的子類,屬性動畫的基類,通過控制可動畫屬性慢慢地變化,不能直接使用。有兩個子類CABasicAnimation 和 CAKeyframeAnimation。 |
CABasicAnimation | CAPropertyAnimation 的子類,基礎動畫,簡單地控制CALayer層的屬性慢慢改變,從而實現動畫。 |
CAKeyframeAnimation | CAPropertyAnimation 的子類,關鍵幀動畫,同樣是通過屬性進行動畫參數控制,通過values屬性指定多個關鍵幀,通過多個關鍵幀可以指定動畫的各階段的關鍵值。 |
基礎動畫、關鍵幀動畫都屬于屬性動畫,是通過修改屬性值產生動畫效果,只需要設置初始值和結束值,中間的過程動畫(“補間動畫”)是由系統自動計算產生。和基礎動畫不同的是關鍵幀動畫可以設置多個屬性值,每兩個屬性中間的補間動畫由系統自動完成,基礎動畫也可以看成是有兩個關鍵幀的關鍵幀動畫。
二、使用
1. CAAnimation
CAAnimation 是基類,其屬性它的子類也擁有。它不能直接使用。
屬性 | 說明 |
---|---|
beginTime | 可以用來設置動畫延遲執行時間,若想延遲2s,就設置為CACurrentMediaTime()+2,CACurrentMediaTime()為圖層的當前時間 |
duration | 動畫的持續時間 |
delegate | 動畫代理 |
repeatCount | 重復次數,無限循環可以設置HUGE_VALF或者MAXFLOAT |
repeatDuration | 重復時間 |
removedOnCompletion | 默認為YES,代表動畫執行完畢后就從圖層上移除,圖形會恢復到動畫執行前的狀態。如果想讓圖層保持顯示動畫執行后的狀態,那就設置為NO,同時還要設置fillMode為kCAFillModeForwards |
fillMode | 決定當前對象在非active時間段的行為。(要想fillMode有效,最好設置removedOnCompletion = NO),kCAFillModeRemoved 這個是默認值,也就是說當動畫開始前和動畫結束后,動畫對layer都沒有影響,動畫結束后,layer會恢復到之前的狀態;kCAFillModeForwards 當動畫結束后,layer會一直保持著動畫最后的狀態;kCAFillModeBackwards 在動畫開始前,只需要將動畫加入了一個layer,layer便立即進入動畫的初始狀態并等待動畫開始;kCAFillModeBoth 這個其實就是上面兩個的合成.動畫加入后開始之前,layer便處于動畫初始狀態,動畫結束后layer保持動畫最后的狀態 |
timingFunction | 速度控制函數,控制動畫運行的節奏,為 CAMediaTimingFunction 類型。kCAMediaTimingFunctionLinear(線性):勻速動畫;kCAMediaTimingFunctionEaseIn(漸進):動畫緩慢進入,然后加速離開;kCAMediaTimingFunctionEaseOut(漸出):動畫全速進入,然后減速的到達目的地;kCAMediaTimingFunctionEaseInEaseOut(漸進漸出):動畫緩慢的進入,中間加速,然后減速的到達目的地。 |
代理方法 : 給NSObject添加了分類,所以任何對象,都可以成為CAAnimation的代理。
動畫開始的時候調用 - (void)animationDidStart:(CAAnimation *)anim;
動畫停止的時候調用 - (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
2.CAPropertyAnimation
屬性或方法 | 說明 |
---|---|
+ (instancetype)animationWithKeyPath:(nullable NSString *)path; | 類方法,創建一個新的動畫對象,同時設置該對象的keyPath屬性 |
keyPath | 屬性,用來描述動畫是作用在哪一個屬性上 |
additive | 屬性,指定該屬性動畫是否以當前動畫效果為基礎 |
cumulative | 屬性,指定動畫是否為累加效果 |
valueFunction | 該屬性值是一個 CAValueFunction 對象,負責對屬性改變進行插值計算,系統已經提供了默認的插值計算方式,一般無須指定該屬性。 |
位移動畫通常使用屬性動畫控制 CALayer 的 position 屬性持續改變,如果要實現 CALayer 的旋轉、縮放等動畫效果,若在平面上(二維空間)上,則修改 CALayer 的 affineTransform 屬性,該屬性值為一個CGAffineTransform 對象;若在三維空間上,則修改 CALayer 的 transform 屬性,該屬性值為一個 CATransform3D 對象。
CATransform3D : 三維變換矩陣
方法 | 說明 |
---|---|
CATransform3DIsIdentity (CATransform3D t) | 返回bool值,判斷 t 矩陣是否為單位矩陣 |
CATransform3DEqualToTransform (CATransform3D a, CATransform3D b) | 返回bool值,判斷 a , b 兩個矩陣是否相等 |
CATransform3DMakeTranslation (CGFloat tx, CGFloat ty, CGFloat tz) | 創建一個在X方向上移動 tx、在Y方向上移動 ty、在Z方向上移動 tz 的變化矩陣 |
CATransform3DMakeScale (CGFloat sx, CGFloat sy, CGFloat sz) | 創建一個在X方向上縮放 sx、在Y方向上縮放 sy、在Z方向上縮放 sz 的變化矩陣 |
CATransform3DMakeRotation (CGFloat angle, CGFloat x, CGFloat y, CGFloat z) | 創建基于指定旋轉軸旋轉 angle 弧度的變換,x、y、z 用于確定旋轉軸,(1,0,0)指定旋轉軸為X軸,(0,1,0)指定旋轉軸為Y軸,(0,0,1)指定旋轉軸為Z軸 |
CATransform3DTranslate (CATransform3D t, CGFloat tx, CGFloat ty, CGFloat tz) | 在 t 變化矩陣的基礎上進行位移變換 |
CATransform3DScale (CATransform3D t, CGFloat sx, CGFloat sy, CGFloat sz) | 在 t 變化矩陣的基礎上進行縮放變換 |
CATransform3DRotate (CATransform3D t, CGFloat angle, CGFloat x, CGFloat y, CGFloat z) | 在 t 變化矩陣的基礎上進行旋轉變換 |
CATransform3DConcat (CATransform3D a, CATransform3D b) | 將a,b兩個矩陣相乘 |
CATransform3DInvert (CATransform3D t) | 對 t 矩形進行反轉 |
CATransform3DMakeAffineTransform (CGAffineTransform m) | 將 CGAffineTransform 矩陣轉化成 CATransform3D 矩陣,轉化后也只有X,Y維度上的變換 |
CATransform3DIsAffine (CATransform3D t) | 返回bool值,判斷 t 矩陣是否是 CGAffineTransform 變換矩陣 |
CATransform3DGetAffineTransform (CATransform3D t) | 獲取 t 矩陣所包含的 CGAffineTransform 變換矩陣 |
3.CABasicAnimation 和 CAKeyframeAnimation
CAPropertyAnimation的子類,首先創建初始化一個CALayer,初始化動畫CABasicAnimation是設置 fromValue 和 toValue ,CAKeyframeAnimation是設置 values ,再設置其他動畫屬性,將動畫添加到圖層上。
CALayer 為動畫提供的方法
方法 | 說明 |
---|---|
- (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key | 為 CALayer 添加一個動畫,并為動畫指定一個唯一標識 |
- (nullable CAAnimation *)animationForKey:(NSString *)key | 讓 CALayer 執行 key 所對應的動畫 |
- (void)removeAllAnimations | 刪除 CALayer 上添加的所有動畫 |
- (void)removeAnimationForKey:(NSString *)key | 刪除 key 所對應的動畫 |
- (nullable NSArray<NSString *> *)animationKeys | 獲取 CALayer 上所有動畫的 key 組成的數組 |
① 位移動畫
CABasicAnimation
CABasicAnimation * anim = [CABasicAnimation animationWithKeyPath:@"position"];
anim.fromValue = [NSValue valueWithCGPoint:self.layer.position];
CGPoint toPoint = CGPointMake(300, 300);
anim.toValue = [NSValue valueWithCGPoint:toPoint];
anim.duration = 1.0;
self.layer.position = toPoint;
anim.removedOnCompletion = YES;
[self.layer addAnimation:anim forKey:@"KCBasicAnimation_Position"];
CAKeyframeAnimation
CAKeyframeAnimation * keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
NSValue * value1 = [NSValue valueWithCGPoint:self.layer.position];
NSValue * value2 = [NSValue valueWithCGPoint:CGPointMake(80, 220)];
NSValue * value3 = [NSValue valueWithCGPoint:CGPointMake(45, 300)];
NSValue * value4 = [NSValue valueWithCGPoint:CGPointMake(55, 400)];
NSArray * values=@[value1,value2,value3,value4];
keyframeAnimation.values = values;
keyframeAnimation.duration = 2.0;
[self.layer addAnimation:keyframeAnimation forKey:@"KCKeyframeAnimation_Position"];
通過描繪路徑進行關鍵幀動畫控制
CAKeyframeAnimation * keyframeAnimation=[CAKeyframeAnimation animationWithKeyPath:@"position"];
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, self.layer.position.x, self.layer.position.y);
CGPathAddCurveToPoint(path, NULL, 160, 280, -30, 300, 55, 400);
keyframeAnimation.path = path;
CGPathRelease(path);
keyframeAnimation.duration = 4.0;
keyframeAnimation.removedOnCompletion = YES;
[self.layer addAnimation:keyframeAnimation forKey:@"KCKeyframeAnimation_Position"];
② 旋轉動畫
CABasicAnimation
CABasicAnimation * anim = [CABasicAnimation animationWithKeyPath:@"transform"];
CATransform3D fromValue = self.layer.transform;
anim.fromValue = [NSValue valueWithCATransform3D:fromValue];
CATransform3D toValue = CATransform3DRotate(fromValue, M_PI, 1, 0, 0);
anim.toValue = [NSValue valueWithCATransform3D:toValue];
anim.duration = 0.5;
self.layer.transform = toValue;
anim.removedOnCompletion = YES;
[self.layer addAnimation:anim forKey:@"KCBasicAnimation_Rotate"];
CAKeyframeAnimation
CAKeyframeAnimation * keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
NSValue * value1 = [NSValue valueWithCATransform3D:self.layer.transform];
NSValue * value2 = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 1, 0, 0)];
NSValue * value3 = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 0, 1, 0)];
NSValue * value4 = [NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 0, 0, 1)];
NSArray * values = @[value1,value2,value3,value4];
keyframeAnimation.values = values;
keyframeAnimation.duration = 2.0;
[self.layer addAnimation:keyframeAnimation forKey:@"KCKeyframeAnimation_Rotate"];
③ 縮放動畫
CABasicAnimation
CABasicAnimation * anim = [CABasicAnimation animationWithKeyPath:@"transform"];
CATransform3D fromValue = self.layer.transform;
anim.fromValue = [NSValue valueWithCATransform3D:fromValue];
CATransform3D toValue = CATransform3DScale(fromValue, 0.5, 0.5, 1);
anim.toValue = [NSValue valueWithCATransform3D:toValue];
anim.duration = 0.5;
self.layer.transform = toValue;
anim.removedOnCompletion = YES;
[self.layer addAnimation:anim forKey:@"KCBasicAnimation_Scale"];
CAKeyframeAnimation
CAKeyframeAnimation * keyframeAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
NSValue * value1 = [NSValue valueWithCATransform3D:self.layer.transform];
NSValue * value2 = [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.5, 0.5, 1)];
NSValue * value3 = [NSValue valueWithCATransform3D:CATransform3DMakeScale(1, 1, 1)];
NSArray * values = @[value1,value2,value3];
keyframeAnimation.values = values;
keyframeAnimation.duration = 2.0;
[self.layer addAnimation:keyframeAnimation forKey:@"KCKeyframeAnimation_Scale"];
④ 動畫組
CABasicAnimation
CGPoint fromPoint = self.layer.position;
CGPoint toPoint = CGPointMake(280, fromPoint.y + 300);
CABasicAnimation * moveAnim = [CABasicAnimation animationWithKeyPath:@"position"];
moveAnim.fromValue = [NSValue valueWithCGPoint:fromPoint];
moveAnim.toValue = [NSValue valueWithCGPoint:toPoint];
moveAnim.removedOnCompletion = YES;
CABasicAnimation * transformAnim = [CABasicAnimation animationWithKeyPath:@"transform"];
CATransform3D fromValue = self.layer.transform;
transformAnim.fromValue = [NSValue valueWithCATransform3D:fromValue];
CATransform3D scaleValue = CATransform3DScale(fromValue, 0.5, 0.5, 1);
CATransform3D rotateValue = CATransform3DRotate(fromValue, M_PI, 0, 0, 1);
CATransform3D toValue = CATransform3DConcat(scaleValue, rotateValue);
transformAnim.toValue = [NSValue valueWithCATransform3D:toValue];
transformAnim.cumulative = YES;
transformAnim.repeatCount = 2;
transformAnim.duration = 3;
CAAnimationGroup * animGroup = [CAAnimationGroup animation];
animGroup.animations = @[moveAnim,transformAnim];
animGroup.duration = 6;
[self.layer addAnimation:animGroup forKey:@"KCBasicAnimation_Group"];
CAKeyframeAnimation
CAKeyframeAnimation * moveAnim = [CAKeyframeAnimation animationWithKeyPath:@"position"];
NSValue * move1 = [NSValue valueWithCGPoint:self.layer.position];
NSValue * move2 = [NSValue valueWithCGPoint:CGPointMake(300, 100)];
NSValue * move3 = [NSValue valueWithCGPoint:CGPointMake(300, 300)];
NSValue * move4 = [NSValue valueWithCGPoint:CGPointMake(100, 300)];
NSValue * move5 = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
NSArray * values = @[move1,move2,move3,move4,move5];
moveAnim.values = values;
moveAnim.removedOnCompletion = YES;
CAKeyframeAnimation * transformAnim = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
CATransform3D rotate1 = self.layer.transform;
CATransform3D rotate2 = CATransform3DMakeRotation(M_PI, 1, 0, 0);
CATransform3D rotate3 = CATransform3DMakeRotation(M_PI, 0, 1, 0);
CATransform3D rotate4 = CATransform3DMakeRotation(M_PI, 0, 0, 1);
CATransform3D rotate5 = CATransform3DMakeRotation(M_PI, 1, 1, 1);
CATransform3D scale1 = self.layer.transform;
CATransform3D scale2 = CATransform3DMakeScale(0.5, 0.5, 1);
CATransform3D scale3 = CATransform3DMakeScale(1, 1, 1);
CATransform3D scale4 = CATransform3DMakeScale(0.5, 0.5, 1);
CATransform3D scale5 = CATransform3DMakeScale(1, 1, 1);
NSValue * transform1 = [NSValue valueWithCATransform3D:CATransform3DConcat(rotate1, scale1)];
NSValue * transform2 = [NSValue valueWithCATransform3D:CATransform3DConcat(rotate2, scale2)];
NSValue * transform3 = [NSValue valueWithCATransform3D:CATransform3DConcat(rotate3, scale3)];
NSValue * transform4 = [NSValue valueWithCATransform3D:CATransform3DConcat(rotate4, scale4)];
NSValue * transform5 = [NSValue valueWithCATransform3D:CATransform3DConcat(rotate5, scale5)];
transformAnim.values = @[transform1,transform2,transform3,transform4,transform5];
transformAnim.removedOnCompletion = YES;
CAAnimationGroup * animGroup = [CAAnimationGroup animation];
animGroup.animations = @[moveAnim,transformAnim];
animGroup.duration = 8;
self.layer.position = CGPointMake(100, 100);
[self.layer addAnimation:animGroup forKey:@"KCBasicAnimation_Group"];
④ 動畫暫停
-(void)pauseLayer{
CFTimeInterval interval=[self.layer convertTime:CACurrentMediaTime() fromLayer:nil];
[self.layer setTimeOffset:interval];
self.layer.speed=0;
}
⑤ 動畫恢復
-(void)resumeLayer{
CFTimeInterval beginTime= CACurrentMediaTime() - self.layer.timeOffset;
self.layer.timeOffset = 0;
self.layer.beginTime = beginTime;
self.layer.speed=1.0;
}
4.CATransition
屬性 | 說明 |
---|---|
type | 動畫過渡效果 |
subtype | 動畫過渡方向 |
startProgress | 動畫起點(在整體動畫的百分比) |
endProgress | 動畫終點(在整體動畫的百分比) |
CATransition的type屬性用于控制動畫類型,還有私有動畫。
值| 說明 |是否支持方向設置
-----|-----
kCATransitionFade|通過漸隱效果過渡,默認屬性值|是
kCATransitionMoveIn|通過移入動畫過渡,新視圖移到舊視圖上面|是
kCATransitionPush|通過推入動畫過渡,新視圖把舊視圖推出去|是
kCATransitionReveal|通過揭開動畫過渡,將舊視圖移開,同時顯示下面的新視圖|是
@"cube"|通過立方體旋轉動畫過渡|是
@"oglFlip"|通過翻轉動畫過渡|是
@"suckEffect"|通過收縮(吸入)動畫過渡|否
@"rippleEffect"|通過水波動畫過渡|否
@"pageCurl"|通過向后翻頁動畫過渡|是
@"pageUnCurl"|通過向前翻頁動畫過渡|是
@"cameraIrisHollowOpen"|通過相機鏡頭打開效果動畫過渡|否
@"cameraIrisHollowClose"|通過相機鏡頭關閉效果動畫過渡|否
CATransition的subtype屬性用于控制動畫方向
值 | 說明 |
---|---|
kCATransitionFromRight | 從右側轉場 |
kCATransitionFromLeft | 從左側轉場 |
kCATransitionFromTop | 從頂部轉場 |
kCATransitionFromBottom | 從底部轉場 |
#import "ViewController.h"
#define IMAGE_COUNT 5
typedef enum {
GestureDirectionUp = 1,
GestureDirectionDown,
GestureDirectionLeft,
GestureDirectionRight
}GestureDirection;
@interface ViewController () <UIPickerViewDelegate,UIPickerViewDataSource>
@property (nonatomic,strong) UIImageView * imageView;
@property (nonatomic,assign) int currentIndex;
@property (nonatomic,strong) CATransition * transition;
@property (nonatomic,strong) UIPickerView * pickerView;
@property (nonatomic,copy) NSArray * transitions;
@property (nonatomic,assign) BOOL isPop;
@end
@implementation ViewController
- (NSArray *)transitions{
if (_transitions == nil) {
_transitions = @[kCATransitionFade,kCATransitionMoveIn,kCATransitionPush,kCATransitionReveal,@"cube",@"oglFlip",@"suckEffect",@"rippleEffect",@"pageCurl",@"pageUnCurl",@"cameralIrisHollowOpen",@"cameraIrisHollowClose"];
}
return _transitions;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
self.navigationItem.title = @"轉場";
UIBarButtonItem * rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"效果" style:UIBarButtonItemStyleDone target:self action:@selector(selectTransition:)];
self.navigationItem.rightBarButtonItem = rightBarButtonItem;
self.imageView = [[UIImageView alloc]initWithFrame:self.view.frame];
self.imageView.backgroundColor = [UIColor whiteColor];
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
self.imageView.image = [UIImage imageNamed:@"0.jpg"];
[self.view addSubview:self.imageView];
self.pickerView = [[UIPickerView alloc]initWithFrame:CGRectMake(0, screenHeight, screenWidth, 300)];
self.pickerView.dataSource = self;
self.pickerView.delegate = self;
[self.view addSubview:self.pickerView];
UISwipeGestureRecognizer * upSwipeGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];
[upSwipeGesture setDirection:UISwipeGestureRecognizerDirectionUp];
[self.view addGestureRecognizer:upSwipeGesture];
UISwipeGestureRecognizer * downSwipeGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];
[downSwipeGesture setDirection:UISwipeGestureRecognizerDirectionDown];
[self.view addGestureRecognizer:downSwipeGesture];
UISwipeGestureRecognizer * leftSwipeGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];
[leftSwipeGesture setDirection:UISwipeGestureRecognizerDirectionLeft];
[self.view addGestureRecognizer:leftSwipeGesture];
UISwipeGestureRecognizer * rightSwipeGesture = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipe:)];
[rightSwipeGesture setDirection:UISwipeGestureRecognizerDirectionRight];
[self.view addGestureRecognizer:rightSwipeGesture];
self.transition = [[CATransition alloc]init];
self.transition.type = kCATransitionFade;
self.isPop = NO;
}
- (void)selectTransition:(UIBarButtonItem *)btn{
self.isPop = !self.isPop;
if (self.isPop) {
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.6];
[self.view bringSubviewToFront:self.pickerView];
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat pickerViewWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat pickerViewHeight = 300;
self.pickerView.frame = CGRectMake(0, screenHeight - pickerViewHeight, pickerViewWidth, pickerViewHeight);
[UIView commitAnimations];
}
else {
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:nil context:context];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.6];
CGFloat screenHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat pickerViewWidth = [UIScreen mainScreen].bounds.size.width;
CGFloat pickerViewHeight = 300;
self.pickerView.frame = CGRectMake(0, screenHeight, pickerViewWidth, pickerViewHeight);
[UIView commitAnimations];
}
}
-(void)swipe:(UISwipeGestureRecognizer *)gesture{
if (gesture.direction == UISwipeGestureRecognizerDirectionUp){
[self transitionAnimation:GestureDirectionUp];
}
else if (gesture.direction == UISwipeGestureRecognizerDirectionDown){
[self transitionAnimation:GestureDirectionDown];
}
else if (gesture.direction == UISwipeGestureRecognizerDirectionLeft){
[self transitionAnimation:GestureDirectionLeft];
}
else {
[self transitionAnimation:GestureDirectionRight];
}
}
- (void)transitionAnimation:(GestureDirection)gestureDirection{
switch (gestureDirection) {
case GestureDirectionUp:
self.transition.subtype = kCATransitionFromTop;
break;
case GestureDirectionDown:
self.transition.subtype = kCATransitionFromBottom;
break;
case GestureDirectionLeft:
self.transition.subtype = kCATransitionFromRight;
break;
case GestureDirectionRight:
self.transition.subtype = kCATransitionFromLeft;
break;
default:
break;
}
self.transition.duration = 1.0f;
self.imageView.image = [self getImage];
[self.view.layer addAnimation:self.transition forKey:@"KCTransitionAnimation"];
}
- (UIImage *)getImage{
self.currentIndex = (self.currentIndex + 1) % IMAGE_COUNT;
NSString *imageName=[NSString stringWithFormat:@"%i.jpg",_currentIndex];
return [UIImage imageNamed:imageName];
}
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;
}
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return self.transitions.count;
}
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
return self.transitions[row];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
self.transition.type = self.transitions[row];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
5.逐幀動畫
通過設置UIImageView的animationImages屬性,然后調用它的startAnimating方法去播放一組逐幀圖片,但是它存在著很大的性能問題,并且這種方法一旦設置完圖片中間的過程就無法控制了。使用CADisplayLink加入到主循環隊列中,循環調用目標方法,在這個方法中更新視圖內容就可以完成逐幀動畫。
- (void)runAnimationWithImageCount:(NSInteger)count AndImageName:(NSString *)name
{
if (self.myImageView.isAnimating)
{
return;
}
NSMutableArray * pictArray = [NSMutableArray array];
for (int i = 0; i < count; i++)
{
NSString * pictNum = [NSString stringWithFormat:@"%@_%02d.jpg",name,i];
// 使用imageNamed的方式加載圖片會有緩存,使得占用的內存很大
//[pictArray addObject:[UIImage imageNamed:pictNum]];
// 直接從文件中讀取圖片就不會占用太大的內存
NSBundle * boudle = [NSBundle mainBundle];
NSString * path = [boudle pathForResource:pictNum ofType:nil];
[pictArray addObject:[UIImage imageWithContentsOfFile:path]];
}
self.myImageView.animationImages = pictArray;
self.myImageView.animationDuration = (self.myImageView.animationImages.count * 0.07);
CGFloat delayTime = self.myImageView.animationDuration + 0.5;
self.myImageView.animationRepeatCount = 1;
[self.myImageView startAnimating];
[self.myImageView performSelector:@selector(setAnimationImages:) withObject:nil afterDelay:delayTime];
}
6.UIView動畫封裝
UIView本身對于基本動畫和關鍵幀動畫、轉場動畫都有相應的封裝。
方法 | 說明 |
---|---|
+ (void)beginAnimations:(nullable NSString *)animationID context:(nullable void *)context | 開始動畫,animationID :動畫塊內部應用程序標識,context :附加的信息,都傳遞給動畫代理 |
+ (void)commitAnimations | 提交動畫,結束動畫 |
+ (void)setAnimationDelegate:(nullable id)delegate | 設置動畫的代理 |
+ (void)setAnimationWillStartSelector:(nullable SEL)selector | 設置動畫開始時的執行方法 |
+ (void)setAnimationDidStopSelector:(nullable SEL)selector | 設置動畫結束時的執行方法 |
+ (void)setAnimationDuration:(NSTimeInterval)duration | 設置動畫執行時間 |
+ (void)setAnimationDelay:(NSTimeInterval)delay | 設置動畫延遲執行的時間 |
+ (void)setAnimationStartDate:(NSDate *)startDate | 設置動畫開始執行的時間 |
+ (void)setAnimationCurve:(UIViewAnimationCurve)curve | 設置動畫運行過程中相對的速度 |
+ (void)setAnimationRepeatCount:(float)repeatCount | 設置動畫重復次數 |
+ (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses | 設置是否自動逆向動畫 |
+ (void)setAnimationBeginsFromCurrentState:(BOOL)fromCurrentState | 設置動畫是否從當前狀態開始執行,設置為YES,則當上一次動畫正在執行中,那么當下一個動畫開始時,上一次動畫的當前狀態將成為下一次動畫的開始狀態。設置為NO,則當上一個動畫正在執行中,那么當下一個動畫開始時,上一次動畫需要先恢復到完成時的狀態,然后在開始執行下一次動畫 |
+ (void)setAnimationsEnabled:(BOOL)enabled | 設置動畫是否可用 |
+ (BOOL)areAnimationsEnabled | 獲取動畫是否可用 |
動畫使用block方式
方法 | 說明 |
---|---|
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations | 使用block方式 |
+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion | 使用block方式,有完成后的block |
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion | 使用block方式,可以設置動畫延遲時間和動畫參數 |
+ (void)transitionWithView:(UIView *)view duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options animations:(void (^ __nullable)(void))animations completion:(void (^ __nullable)(BOOL finished))completion | 轉場動畫 |
+ (void)transitionFromView:(UIView *)fromView toView:(UIView *)toView duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options completion:(void (^ __nullable)(BOOL finished))completion | 轉場動畫,toView添加到fromView的父視圖中,fromView從它的福視圖中移除 |
+ (void)animateKeyframesWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay options:(UIViewKeyframeAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion | 使用block方式實現關鍵幀動畫 |
+ (void)addKeyframeWithRelativeStartTime:(double)frameStartTime relativeDuration:(double)frameDuration animations:(void (^)(void))animations | 從開始時間添加一個持續時間的動畫 |
UIViewAnimationCurve
值 | 說明 |
---|---|
UIViewAnimationCurveEaseIn | 先慢后快,在動畫剛開始的時候執行速度慢 |
UIViewAnimationCurveEaseOut | 先快后慢,在動畫快結束的時候執行速度慢 |
UIViewAnimationCurveEaseInOut | 先由慢變快,然后再變慢,在動畫剛開始和快結束的時候速度慢 |
UIViewAnimationCurveLinear | 勻速運行 |
UIViewAnimationOptions
值 | 說明 |
---|---|
UIViewAnimationOptionLayoutSubviews | 動畫過程中保證子視圖跟隨運動 |
UIViewAnimationOptionAllowUserInteraction | 動畫過程中允許用戶交互 |
UIViewAnimationOptionBeginFromCurrentState | 所有視圖從當前狀態開始運行 |
UIViewAnimationOptionRepeat | 重復執行動畫 |
UIViewAnimationOptionAutoreverse | 逆向動畫,動畫運行到結束點后仍然以動畫方式回到初始點 |
UIViewAnimationOptionOverrideInheritedDuration | 忽略嵌套動畫時間設置 |
UIViewAnimationOptionOverrideInheritedCurve | 忽略嵌套動畫速度設置 |
UIViewAnimationOptionAllowAnimatedContent | 動畫過程允許重繪視圖(僅適用在轉場動畫中) |
UIViewAnimationOptionShowHideTransitionViews | 視圖切換時翻轉隱藏舊視圖、顯示新視圖,而不再是直接刪除舊視圖、添加新視圖 |
UIViewAnimationOptionOverrideInheritedOptions | 不繼承任何參數或動畫類型 |
UIViewAnimationOptionCurveEaseInOut | 默認值,和UIViewAnimationCurveEaseInOut效果一樣,只是這個值用于block動畫參數,先由慢變快,然后再變慢,在動畫剛開始和快結束的時候速度慢 |
UIViewAnimationOptionCurveEaseIn | 和UIViewAnimationCurveEaseIn效果一樣,先慢后快,在動畫剛開始的時候執行速度慢 |
UIViewAnimationOptionCurveEaseOut | 和UIViewAnimationCurveEaseOut效果一樣,先快后慢,在動畫快結束的時候執行速度慢 |
UIViewAnimationOptionCurveLinear | 和UIViewAnimationCurveLinear效果一樣,勻速運行 |
UIViewAnimationOptionTransitionNone | 下面的值僅適用于轉場動畫設置,沒有轉場動畫效果 |
UIViewAnimationOptionTransitionFlipFromLeft | 從左側翻轉 |
UIViewAnimationOptionTransitionFlipFromRight | 從右側翻轉 |
UIViewAnimationOptionTransitionCurlUp | 向后翻頁的動畫過渡 |
UIViewAnimationOptionTransitionCrossDissolve | 水波動畫過渡 |
UIViewAnimationOptionTransitionFlipFromTop | 從頂部翻轉 |
UIViewAnimationOptionTransitionFlipFromBottom | 從底部翻轉 |
UIViewKeyframeAnimationOptions
其中一部分值和 UIViewAnimationOptions 是一樣的,不一樣的如下:
值 | 說明 |
---|---|
UIViewKeyframeAnimationOptionCalculationModeLinear | 連續運算模式 |
UIViewKeyframeAnimationOptionCalculationModeDiscrete | 離散運算模式 |
UIViewKeyframeAnimationOptionCalculationModePaced | 均勻執行運算模式 |
UIViewKeyframeAnimationOptionCalculationModeCubic | 平滑運算模式 |
UIViewKeyframeAnimationOptionCalculationModeCubicPaced | 平滑均勻運算模式 |
7.彈簧動畫
方法 | 說明 |
---|---|
+ (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion | UIViewAnimationWithBlocks中的方法,用來創建具有彈簧效果的動畫。dampingRatio為阻尼,范圍0~1,阻尼越接近于0,彈性效果越明顯。velocity為彈性復位的速度。 |
[UIView animateWithDuration:5.0 delay:0 usingSpringWithDamping:0.1 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveLinear animations:^{
self.imageView.center = CGPointMake(300, 300);
} completion:nil];