Core Animation,中文翻譯爲核心動畫,它是一組很是強大的動畫處理API,使用它能作出很是炫麗的動畫效果,並且每每是事半功倍。也就是說,使用少許的代碼就能夠實現很是強大的功能。函數
Core Animation是跨平臺的,能夠用在Mac OS X和iOS平臺。動畫
Core Animation的動畫執行過程都是在後臺操做的,不會阻塞主線程。不阻塞主線程,能夠理解爲在執行動畫的時候還能點擊(按鈕)。atom
CAAnimation 簡介:url
CAAimation
是全部動畫對象的基類,負責控制動畫的持續時間和速度,是個抽象類,不能直接使⽤用,應該使⽤用它具體的⼦子類
屬性說明:spa
removedOnCompletion
// 動畫是否⾃自動移出線程
duration
// 動畫持續時間翻譯
speed
//速度代理
timeOffset
// 動畫時間的偏移code
repeatCount
// 動畫重複執⾏行的次數(HUGE_VALF無限次)orm
repeatDuration
// 動畫重複執⾏行的總時間
autoreverses
// 反轉動畫
delegate
// 代理
fillMode
// 填充模式
timingFunction
//速度控制函數
基本屬性說明:
屬性 | 說明 |
---|---|
duration |
動畫的持續時間 |
repeatCount |
重複次數,無限循環能夠設置HUGE_VALF或者MAXFLOAT |
repeatDuration |
重複時間 |
removedOnCompletion |
默認爲YES,表明動畫執行完畢後就從圖層上移除,圖形會恢復到動畫執行前的狀態。若是想讓圖層保持顯示動畫執行後的狀態,那就設置爲NO,不過還要設置 |
fillMode |
決定當前對象在非active時間段的行爲。好比動畫開始以前或者動畫結束之 |
beginTime |
能夠用來設置動畫延遲執行時間,若想延遲2s,就設置爲CACurrentMediaTime()+2,CACurrentMediaTime()爲圖層的當前時間 |
timingFunction |
速度控制函數,控制動畫運行的節奏 |
delegate |
動畫代理 |
fillMode
屬性的設置:
kCAFillModeRemoved 這個是默認值,也就是說當動畫開始前和動畫結束後,動畫對layer都沒有影響,動畫結束後,layer會恢復到以前的狀態kCAFillModeForwards 當動畫結束後,layer會一直保持着動畫最後的狀態
kCAFillModeBackwards 在動畫開始前,只須要將動畫加入了一個layer,layer便當即進入動畫的初始狀態並等待動畫開始。
kCAFillModeBoth 這個其實就是上面兩個的合成.動畫加入後開始以前,layer便處於動畫初始狀態,動畫結束後layer保持動畫最後的狀態
速度控制函數(CAMediaTimingFunction):
kCAMediaTimingFunctionLinear(線性):勻速,給你一個相對靜態的感受
kCAMediaTimingFunctionEaseIn(加速):動畫緩慢進入,而後加速離開
kCAMediaTimingFunctionEaseOut(減速):動畫全速進入,而後減速的到達目的地
kCAMediaTimingFunctionEaseInEaseOut(漸進漸出):動畫緩慢的進入,中間加速,而後減速的到達目的地。這個是默認的動畫行爲。
CABasicAnimation 用於實現layer屬性值從一個值(fromValue)到另一個值(toValue)變化的簡單動畫,好比旋轉、縮放、逐漸透明、移動等。
相關屬性:
fromValue:keyPath相應屬性的初始值
byValue:keyPath相應屬性的中間值( 變化的值)
toValue:keyPath相應屬性的結束值
動畫過程說明:
隨着動畫的進⾏行,在⻓長度爲duration的持續時間內,keyPath相應屬性的值從
fromValue漸漸地變爲toValue。
keyPath內容是CALayer的可動畫Animatable屬性
若是fillMode=kCAFillModeForwards同時removedOnComletion=NO,那麼
在動畫執行完畢後,圖層會保持顯示動畫執行後的狀態。但實質上,圖層屬性值仍是動畫執行前的初始值,並無真正被改變。
代碼事例以下:
#import "ViewController.h" @interface ViewController (){ CALayer *aniLayer; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; aniLayer = [CALayer layer]; aniLayer.frame = CGRectMake(100, 50, 100, 100); aniLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"足球"].CGImage); [self.view.layer addSublayer:aniLayer]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { // 給layer 添加動畫 [aniLayer addAnimation:[self positionAnimation] forKey:@"position"]; [aniLayer addAnimation:[self rotationAnimation] forKey:@"rotation"]; // [aniLayer addAnimation:[self boundsAnimation] forKey:@"bounds"]; // [aniLayer addAnimation:[self scaleAnimation] forKey:@"scale"]; } - (CAAnimation *)positionAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; animation.fromValue = [NSValue valueWithCGPoint:CGPointMake(150, 50)]; animation.toValue = [NSValue valueWithCGPoint:CGPointMake(150, 550)]; animation.duration = 2.0; animation.fillMode = kCAFillModeForwards; animation.removedOnCompletion = NO; animation.autoreverses = YES; animation.repeatCount = HUGE_VALF; // HUGE_VALF 最大浮點數,表示無限次重複 /* 動畫的線性變換(動畫速度變化) kCAMediaTimingFunctionLinear 勻速 kCAMediaTimingFunctionEaseIn 加速 kCAMediaTimingFunctionEaseOut 減速 kCAMediaTimingFunctionEaseInEaseOut 緩慢進入緩慢出去 kCAMediaTimingFunctionDefault 默認 */ animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; return animation; } - (CAAnimation *)boundsAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"bounds"]; animation.fromValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 100, 100)]; animation.toValue = [NSValue valueWithCGRect:CGRectMake(0, 0, 300, 300)]; animation.duration = 2.0; return animation; } - (CAAnimation *)rotationAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; // animation.fromValue = @0; // animation.toValue = @(2 * M_PI); animation.byValue = @( -2 * M_PI); animation.duration = 2.0; animation.fillMode = kCAFillModeForwards; animation.removedOnCompletion = NO; animation.autoreverses = YES; animation.repeatCount = HUGE_VALF; // HUGE_VALF 最大浮點數,表示無限次重複 return animation; } - (CAAnimation *)scaleAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.scale"]; // 一、初始值 animation.fromValue = @1.0; // 二、目標值 animation.toValue = @2.0; // 三、變化的值, fromValue ~ toValue 值的變化量 // animation.byValue = @1.0; // 四、動畫時間 animation.duration = 2.0; /* 五、動畫的填充模式: kCAFillModeForwards kCAFillModeBackwards kCAFillModeBoth kCAFillModeRemoved */ animation.fillMode = kCAFillModeForwards; // 六、動畫後是否移除動畫後的狀態(回到原始狀態),默認是YES, 前提是要設置fillModle爲:kCAFillModeForwards animation.removedOnCompletion = NO; // 七、是否有回覆效果 animation.autoreverses = YES; // 八、設置動畫重複次數 animation.repeatCount = HUGE_VALF; // HUGE_VALF 最大浮點數,表示無限次重複 // 九、播放動畫的速度 animation.speed = 2; return animation; } @end
暫停動畫:
-(void)stopAnimation{ // CACurrentMediaTime(): 當前媒體時間,表示系統啓動後到當前的秒數,當系統重啓後這個時間也重置 CFTimeInterval stopTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil]; // 設置動畫的時間的偏移 layer.timeOffset = stopTime; layer.speed = 0; }
繼續動畫:
-(void)resumeAnimation{ // 獲取暫停時的時間 CFTimeInterval stopTime = [layer timeOffset]; layer.speed = 1; layer.timeOffset = 0; layer.beginTime = 0; // 設置開始的時間(繼續動畫,這樣設置至關於讓動畫等待的秒數等於暫停的秒) layer.beginTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - stopTime; }
代碼說明:
設置的animationWithKeyPath
是@"position",說明要修改的是CALayer的position屬性,也就是會執行平移動畫
animation.fromValue
,animation.toValue
這裏的屬性接收的時id類型的參數,所以並不能直接使用CGPoint這種結構體類型,而是要先包裝成NSValue對象後再使用。
默認狀況下,動畫執行完畢後,動畫會自動從CALayer上移除,CALayer又會回到原來的狀態。爲了保持動畫執行後的狀態,能夠加入animation.removedOnCompletion = NO
CAKeyframeAnimation 能夠給一個圖層提供多個目標值(values)或者一個指定路徑(path)的動畫。關鍵幀動畫有以下幾個重要參數:
values
:指定圖層屬性(position、scale、rotation...)的多個目標值,這個圖層就會由這些指定的值進行動畫。
path
:一個CGPathRef
類型的路徑,指定圖層就會沿着這個路徑進行動畫。
keyTimes
:關鍵幀指定對應的時間點,其取值範圍爲0到1.0。也就是keyTimes中的每個時間值都對應values中的每一幀.當keyTimes沒有設置的時候,各個關鍵幀的時間平分。
代碼事例以下:
#import "ViewController.h" #define TScreenWidth [UIScreen mainScreen].bounds.size.width #define TScreenHeight [UIScreen mainScreen].bounds.size.height @interface ViewController (){ CALayer *aniLayer; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; CALayer *bgLayer = [CALayer layer]; bgLayer.frame = self.view.bounds; bgLayer.backgroundColor = [UIColor blackColor].CGColor; [self.view.layer addSublayer:bgLayer]; bgLayer.delegate = self; // 重繪 [bgLayer setNeedsDisplay]; aniLayer = [CALayer layer]; aniLayer.bounds = CGRectMake(0 , 0, 100, 100); aniLayer.position = CGPointMake(TScreenWidth / 2, 50); aniLayer.contents = (__bridge id _Nullable)([UIImage imageNamed:@"足球"].CGImage); [self.view.layer addSublayer:aniLayer]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [aniLayer addAnimation:[self keyframeAnimation] forKey:@"keyAnimation"]; } - (CAAnimation *)keyframeAnimation { CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; keyAnimation.duration = 4; // keyAnimation.autoreverses = YES; keyAnimation.repeatCount = HUGE_VALF; keyAnimation.fillMode = kCAFillModeForwards; keyAnimation.removedOnCompletion = NO; /* NSValue *point_1 = [NSValue valueWithCGPoint:CGPointMake(TScreenWidth / 2,0)]; NSValue *point_2 = [NSValue valueWithCGPoint:CGPointMake(50,TScreenHeight / 2)]; NSValue *point_3 = [NSValue valueWithCGPoint:CGPointMake(TScreenWidth / 2,TScreenHeight - 50)]; NSValue *point_4 = [NSValue valueWithCGPoint:CGPointMake(TScreenWidth - 50,TScreenHeight / 2)]; NSValue *point_5 = [NSValue valueWithCGPoint:CGPointMake(TScreenWidth / 2,0)]; // values:設置關鍵幀(多個目標點) keyAnimation.values = @[point_1,point_2,point_3,point_4,point_5]; // 設置每一幀所在的時間比例 keyAnimation.keyTimes = @[@0, @0.2, @0.5, @0.6,@1.0]; */ /* 插值計算模式: kCAAnimationLinear 關鍵幀之間進行插值計算(線性的) kCAAnimationDiscrete 關鍵幀之間不進行插值計算(離散的) kCAAnimationPaced 關鍵幀之間勻速切換,keyTimes\timingFunctions的設置將不起做用 kCAAnimationCubic 關鍵幀進行圓滑曲線相連後插值計算 kCAAnimationCubicPaced 勻速而且關鍵幀進行圓滑曲線相連後插值計算 */ keyAnimation.calculationMode = kCAAnimationLinear; keyAnimation.path = [self path].CGPath; return keyAnimation; } // 繪製路徑 - (UIBezierPath *)path { // 橢圓 // UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:self.view.bounds]; // 圓角矩形 // UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:self.view.bounds cornerRadius:50]; // 內切圓 // UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 100, 300, 300)]; // 貝塞爾曲線 UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(0, TScreenHeight)]; CGPoint point_1 = CGPointMake(TScreenWidth, TScreenHeight); CGPoint controPoint_1 = CGPointMake(TScreenWidth / 2, - TScreenHeight); // CGPoint controPoint_2 = CGPointMake(TScreenWidth / 4 * 3, TScreenHeight); [path addQuadCurveToPoint:point_1 controlPoint:controPoint_1]; // [path addCurveToPoint:point_1 controlPoint1:controPoint_1 controlPoint2:controPoint_2]; return path; } // 繪圖 - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx { CGContextAddPath(ctx , [self path].CGPath); CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor); CGContextSetLineWidth(ctx, 5); CGContextDrawPath(ctx, kCGPathStroke); } @end
CAKeyframeAnimation——計算模式屬性
在關鍵幀動畫中還有一個很是重要的參數,那即是calculationMode,所謂計算模式:其主要針對的是每一幀的內容爲一個座標點的狀況,也就是對anchorPoint和 position進行的動畫
當在平面座標系中有多個離散的點的時候,能夠是離散的,也能夠直線相連後進行插值計算,也可使用圓滑的曲線將他們相連後進行插值計算
calculationMode目前提供以下幾種模式:
kCAAnimationLinear: 默認值,表示當關鍵幀爲座標點的時候,關鍵幀之間直接直線相連進行插值計算
kCAAnimationDiscrete: 離散的,不進行插值計算,全部關鍵幀直接逐個進行顯示
kCAAnimationPaced :使得動畫均勻進行,而不是按keyTimes設置的或者按關鍵幀平分時間,此時keyTimes和timingFunctions無效
kCAAnimationCubic: 對關鍵幀爲座標點的關鍵幀進行圓滑曲線相連後插值計算,這裏的主要目的是使得運行的軌跡變得圓滑
kCAAnimationCubicPaced :看這個名字就知道和kCAAnimationCubic有必定聯繫,其實就是在kCAAnimationCubic的基礎上使得動畫運行變得均勻,就是系統時間內運動的距離相同,此時keyTimes以及timingFunctions也是無效的
CATransition是CAAnimation的子類,用於作轉場動畫
可以爲圖層提供移出屏幕和移入屏幕的動畫效果。iOS比Mac OS X的轉場動畫效果少一點
如:UINavigationController導航控制器就是經過CATransition轉場動畫實現了將控制器的視圖推入屏 幕的動畫效果
轉場動畫就是從一個場景以動畫的形式過渡到另外一個場景。轉場動畫的使用通常分爲如下幾個步驟:
1.建立轉場動畫 CATransition
2.設置轉場類型transtion.type、子類型transtion.subtype(可選)及其餘屬性
3.設置轉場後的新視圖並添加動畫到圖層
下表列出了經常使用的轉場類型(注意私有API是蘋果官方沒有公開的動畫類型,可是目前經過仍然可使用):
fade 淡出效果 kCATransitionFade
movein 新視圖移動到舊視圖上 kCATransitionMoveIn
push 新視圖退出舊視圖上 kCATransitionPush
reveal 移開舊視圖顯示新視圖 kCATransitionReveal
cube 立體翻轉效果
oglFlip 翻轉效果
suckEffect 收縮效果
rippleEffect 水滴波紋效果
pageCurl 向上翻頁效果
pageUnCurl 向下翻頁效果
cameralIrisHollowOpen 攝像頭打開效果
cameraIrisHollowClose 攝像頭關閉效果
代碼事例以下:
#import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UIImageView *imageView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { static int index = 1; [self.imageView.layer addAnimation:[self transitionAnimation] forKey:nil]; NSString *imageName = [NSString stringWithFormat:@"large_%d.jpg",index]; self.imageView.image = [UIImage imageNamed:imageName]; index++; if (index > 10) { index = 0; } } - (CAAnimation *)transitionAnimation { CATransition *transitionAni = [CATransition animation]; transitionAni.duration = 1.0; /* 1. fade 淡出效果 2. moveIn 進入效果 3. push 推出效果 4. reveal 移出效果 // 未公開的 5. cube 立方體翻轉效果 6. suckEffect 抽走效果 7. rippleEffect 水波效果 8. pageCurl 翻開頁效果 9. pageUnCurl 關閉頁效果 10. cameraIrisHollowOpen 相機鏡頭打開效果 11. cameraIrisHollowClose 相機鏡頭關閉效果 */ transitionAni.type = kCATransitionPush; // transitionAni.type = @"push"; // 轉場的方向:`fromLeft', `fromRight', `fromTop' `fromBottom' transitionAni.subtype = @"fromTop"; // 開始轉場和結束轉場的進度位置 // transitionAni.startProgress = 0.5; // transitionAni.endProgress = 1; return transitionAni; } @end
CATransition是CAAnimation的子類,用於作轉場動畫,可以爲層提供移出屏幕和移入屏幕的動畫效果。iOS比Mac OS X的轉場動畫效果少一點。
UINavigationController就是經過CATransition實現了將控制器的視圖推入屏幕的動畫效果。
2.subtype:動畫過渡⽅方向
3.startProgress:動畫起點
4.endProgress:動畫終點
1.kCATransitionFade:淡⼊入淡出
2.kCATransitionMoveIn:新視圖移到舊視圖上⾯面
3.kCATransitionPush:新視圖把舊視圖推出
4.kCATransitionReveal:把舊視圖移開,顯⽰示下⾯面的新視圖
代碼事例以下:
#import "ViewController.h" #define TScreenWidth [UIScreen mainScreen].bounds.size.width #define TScreenHeight [UIScreen mainScreen].bounds.size.height @interface ViewController () @property (weak, nonatomic) IBOutlet UIView *bgView; @property (weak, nonatomic) IBOutlet UIImageView *aniImageView; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; self.bgView.layer.delegate = self; [self.bgView.layer setNeedsDisplay]; NSLog(@"%@",NSStringFromCGRect(self.aniImageView.frame)); NSLog(@"%f, %f",TScreenWidth, TScreenHeight); } - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self.aniImageView.layer addAnimation:[self animationGroup] forKey:@"group"]; } - (CAAnimation *)positionAnimation { CAKeyframeAnimation *keyAni = [CAKeyframeAnimation animationWithKeyPath:@"position"]; keyAni.duration = 3.0; keyAni.fillMode = kCAFillModeForwards; keyAni.removedOnCompletion = NO; keyAni.path = [self path].CGPath; return keyAni; } - (CAAnimation *)rotationAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"]; animation.beginTime = 3.0; animation.duration = 2.0; animation.fillMode = kCAFillModeForwards; animation.removedOnCompletion = NO; animation.byValue = @(2 * M_PI); return animation; } - (CAAnimation *)downAnimation { CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"]; animation.fillMode = kCAFillModeForwards; animation.removedOnCompletion = NO; animation.beginTime = 3.0; animation.duration = 2.0; animation.byValue = [NSValue valueWithCGPoint:CGPointMake(0, TScreenHeight)]; return animation; } - (UIBezierPath *)path { UIBezierPath *path = [UIBezierPath bezierPath]; [path moveToPoint:CGPointMake(0, TScreenHeight)]; CGPoint toPoint = CGPointMake(TScreenWidth, 0); CGPoint controlPoint = CGPointMake(TScreenWidth,TScreenHeight); [path addQuadCurveToPoint:toPoint controlPoint:controlPoint]; return path; } - (CAAnimation *)animationGroup { // 建立一個動畫組,用於組合多種動畫 CAAnimationGroup * aniGroup = [CAAnimationGroup animation]; // 動畫組的完成時間 aniGroup.duration = 5.0; aniGroup.fillMode = kCAFillModeForwards; aniGroup.removedOnCompletion = NO; // 組合多個動畫 aniGroup.animations = @[[self positionAnimation],[self rotationAnimation],[self downAnimation]]; return aniGroup; } - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx { CGContextAddPath(ctx, [self path].CGPath); CGContextSetLineWidth(ctx, 5); CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor); CGContextDrawPath(ctx, kCGPathStroke); } @end