iOS——Core Animation(核心動畫)

一、核心動畫的基本概念

Core Animation,中文翻譯爲核心動畫,它是一組很是強大的動畫處理API,使用它能作出很是炫麗的動畫效果,並且每每是事半功倍。也就是說,使用少許的代碼就能夠實現很是強大的功能。函數

Core Animation是跨平臺的,能夠用在Mac OS X和iOS平臺。動畫

Core Animation的動畫執行過程都是在後臺操做的,不會阻塞主線程。不阻塞主線程,能夠理解爲在執行動畫的時候還能點擊(按鈕)。atom

CAAnimation 簡介:url

CAAimation是全部動畫對象的基類,負責控制動畫的持續時間和速度,是個抽象類,不能直接使⽤用,應該使⽤用它具體的⼦子類
屬性說明:spa

  1. removedOnCompletion // 動畫是否⾃自動移出線程

  2. duration // 動畫持續時間翻譯

  3. speed //速度代理

  4. timeOffset // 動畫時間的偏移code

  5. repeatCount // 動畫重複執⾏行的次數(HUGE_VALF無限次)orm

  6. repeatDuration // 動畫重複執⾏行的總時間

  7. autoreverses // 反轉動畫

  8. delegate // 代理

  9. fillMode // 填充模式

  10. timingFunction //速度控制函數

  基本屬性說明:

屬性 說明

duration

動畫的持續時間

repeatCount

重複次數,無限循環能夠設置HUGE_VALF或者MAXFLOAT

repeatDuration

重複時間

removedOnCompletion

默認爲YES,表明動畫執行完畢後就從圖層上移除,圖形會恢復到動畫執行前的狀態。若是想讓圖層保持顯示動畫執行後的狀態,那就設置爲NO,不過還要設置fillModekCAFillModeForwards

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)

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)

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)

  • CATransition是CAAnimation的子類,用於作轉場動畫

  • 可以爲圖層提供移出屏幕和移入屏幕的動畫效果。iOS比Mac OS X的轉場動畫效果少一點

     如:UINavigationController導航控制器就是經過CATransition轉場動畫實現了將控制器的視圖推入屏   幕的動畫效果

轉場動畫就是從一個場景以動畫的形式過渡到另外一個場景。轉場動畫的使用通常分爲如下幾個步驟:
 
 1.建立轉場動畫 CATransition
 
 2.設置轉場類型transtion.type、子類型transtion.subtype(可選)及其餘屬性
 
 3.設置轉場後的新視圖並添加動畫到圖層
 
 下表列出了經常使用的轉場類型(注意私有API是蘋果官方沒有公開的動畫類型,可是目前經過仍然可使用):

  •  公開的API

    fade 淡出效果  kCATransitionFade
    movein  新視圖移動到舊視圖上   kCATransitionMoveIn
    push 新視圖退出舊視圖上  kCATransitionPush
    reveal 移開舊視圖顯示新視圖   kCATransitionReveal

  •     私有的API

    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

五、動畫組(CAAnimationGrup)

CATransition是CAAnimation的子類,用於作轉場動畫,可以爲層提供移出屏幕和移入屏幕的動畫效果。iOS比Mac OS X的轉場動畫效果少一點。

UINavigationController就是經過CATransition實現了將控制器的視圖推入屏幕的動畫效果。

  •  相關屬性:  1.type:動畫過渡類型

                  2.subtype:動畫過渡⽅方向

                  3.startProgress:動畫起點

                  4.endProgress:動畫終點

  •  type屬性說明:

                   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
相關文章
相關標籤/搜索