1. 程式人生 > >iOS CoreAnimation 基礎動畫CABasicAnimation

iOS CoreAnimation 基礎動畫CABasicAnimation

效果:


Core Animation

 *  
    iOS 核心動畫的實現
 

    CoreAnimation (包含在Quartz Core 框架中), 在iOS核心動畫分為幾類(基礎動畫, 關鍵幀動畫, 動畫組, 轉場動畫, )

    CAAnimation : 核心動畫的基礎類, 不能直接用, 負責動畫執行時間嗎速度的控制, 實現了CAMediaTiming協議

    CAPropertyAnimation : 屬性動畫的基類 (通過屬性進行動畫設定, 注意是可動畫屬性), 不能直接使用

    CAAnimationGroup : 動畫組, 動畫組是一種組合模式設計, 可以通過動畫組來進行所有動畫行為的統一控制, 組中所有動畫效果可以併發執行(一起執行)

    CATransition : 轉場動畫, 主要通過濾鏡進行動畫效果設定

    CABasicAnimation : 基礎動畫, 通過屬性修改進行動畫引數控制, 只有初始狀態和結束狀態

    CAKeyframeAnimation : 關鍵幀動畫, 同樣是通屬性進行動畫採納數控制, 但同基礎動畫不同的是他可以多個狀態控制
 

    基礎動畫, 關鍵幀動畫, 都屬於屬性動畫, 就是通過修改屬性值產生動畫效果, 開發人員只需要設定初始值和結束值, 中間過程動畫(補間動畫)由系統自動計算產生, 和基礎動畫不同的是關鍵幀動畫可以設定多個屬性值, 每兩個屬性中間的補間動畫由系統自定完成, 因此從這個角度基礎動畫是又來個關鍵幀的關鍵幀動畫

直接上程式碼,註釋很全, 簡單易懂:

<span style="font-size:24px;">//
//  ViewController.m
//  CoreAnimation
//
//  Created by 帝炎魔 on 16/5/25.
//  Copyright © 2016年 帝炎魔. All rights reserved.
//

#import "ViewController.h"

#define WIDTH 50 // 寬度

@interface ViewController ()

@property (nonatomic, strong) CALayer *basicAniLayer;

@end

@implementation ViewController



- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    // 小圓球的動畫
  //  [self drawMyLayer];
    
    // 使用代理方法繪圖
   //  [self drawRollImageView];
    
    // 基礎動畫
    [self BasiceAnimation];
    
}


/**
 *  Core Animation
 *  
    iOS 核心動畫的實現
 
    CoreAnimation (包含在Quartz Core 框架中), 在iOS核心動畫分為幾類(基礎動畫, 關鍵幀動畫, 動畫組, 轉場動畫, )
    CAAnimation : 核心動畫的基礎類, 不能直接用, 負責動畫執行時間嗎速度的控制, 實現了CAMediaTiming協議
    CAPropertyAnimation : 屬性動畫的基類 (通過屬性進行動畫設定, 注意是可動畫屬性), 不能直接使用
    CAAnimationGroup : 動畫組, 動畫組是一種組合模式設計, 可以通過動畫組來進行所有動畫行為的統一控制, 組中所有動畫效果可以併發執行(一起執行)
    CATransition : 轉場動畫, 主要通過濾鏡進行動畫效果設定
    CABasicAnimation : 基礎動畫, 通過屬性修改進行動畫引數控制, 只有初始狀態和結束狀態
    CAKeyframeAnimation : 關鍵幀動畫, 同樣是通屬性進行動畫採納數控制, 但同基礎動畫不同的是他可以多個狀態控制
 
    基礎動畫, 關鍵幀動畫, 都屬於屬性動畫, 就是通過修改屬性值產生動畫效果, 開發人員只需要設定初始值和結束值, 中間過程動畫(補間動畫)由系統自動計算產生, 和基礎動畫不同的是關鍵幀動畫可以設定多個屬性值, 每兩個屬性中間的補間動畫由系統自定完成, 因此從這個角度基礎動畫是又來個關鍵幀的關鍵幀動畫
 */


#pragma mark --- 基礎動畫
/**
 *  動畫建立的步驟
 *
    1. 初始化動畫並設定動畫屬性
    2. 設定動畫屬性初始值(可以省略), 結束值以及其他動畫屬性
    3. 給圖層新增動畫
 */
- (void)BasiceAnimation
{
    // 設定背景  (注意這個圖片是在根檢視)
    UIImage *backImage = [UIImage imageNamed:@"haha1"];
    self.view.backgroundColor = [UIColor colorWithPatternImage:backImage];
    
    // 自定義一個圖層
    _basicAniLayer = [[CALayer alloc] init];
    _basicAniLayer.bounds = CGRectMake(0, 0, 50, 50 );
    _basicAniLayer.position = CGPointMake(50, 150);
    _basicAniLayer.contents = (id)[UIImage imageNamed:@"hudie"].CGImage;
    [self.view.layer addSublayer:_basicAniLayer];
    
    
}


#pragma mark ---- 移動動畫
- (void)translationAnimation:(CGPoint)location
{
    // 1.建立動畫並制定動畫屬性(通過keyPath 制定calayer的某個屬性動畫)
    CABasicAnimation *basicAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
    
    // 2. 設定其他動畫屬性初始值和結束值
    basicAnimation.fromValue = [NSValue valueWithCGPoint:_basicAniLayer.position]; // 可以不設定, 預設為圖層初始值狀態
    basicAnimation.toValue = [NSValue valueWithCGPoint:location];
    
  NSLog(@"%f----%f", _basicAniLayer.position.x, _basicAniLayer.position.y);
    
    // 設定其他動畫屬性
    basicAnimation.duration = 2.0; // 動畫時間2秒
    
   // basicAnimation.repeatCount = HUGE_VALF; // 設定動畫重複的次數 HUGE_VALF代表無限大,無線重複動畫
   // basicAnimation.removedOnCompletion = NO; // 執行一次是否移除動畫
    
    
    
    
     /**
       *  這個動畫存在一個問題: 動畫結束後畫圖層回到了原來的位置, 當然用UIVieW封裝的方法是沒有這個問題的, 如何解決這個問題呢
       
       *   圖層動畫的本質就是將圖層內部的內容轉化為點陣圖硬體操作形成一種動畫效果, 其實圖層本身並沒有任何的變化,上面的動畫中圖層並沒有因為動畫效果而改變它的位置, (對於縮放動畫其大小也是不會改變的), 所以在動畫完成之後, 圖層的屬性也不會有任何變化, 我們可以在動畫完成之後重新設定它的位置
       */
    
    basicAnimation.delegate = self;
    
    // 儲存當前位置在動畫結束後使用

    [basicAnimation setValue:[NSValue valueWithCGPoint:location] forKey:@"KCBasicAnimationLocation"];
    
    
    
    // 新增動畫到圖層, 注意key相當於給動畫進行命名, 以後獲得該動畫是可以使用此名稱獲取
    [_basicAniLayer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Translation"];
    
    
    // 在touch 點選事件中 執行動畫
    
    
    
}

#pragma mark ---- 旋轉動畫
/**
 *  圖層的形變都是基於錨點進行的
 *  通過keyPath 對錨點進行改變, 旋轉的中心點就是圖層的錨點
 */

- (void)rotationAnimation
{
     // 1. 建立動畫並制定動畫屬性
    CABasicAnimation *basicAnimation  = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    
    // 2. 設定動畫屬性初始值, 終止值
    basicAnimation.fromValue = [NSNumber numberWithInt:M_PI_2];
    basicAnimation.toValue = [NSNumber numberWithInt:M_PI_2 * 3];
    
    // 3. 設定其他動畫屬性
    basicAnimation.duration = 6.0;
    // 設定旋轉後再旋轉到原來的位置
    basicAnimation.autoreverses = true;
    
    basicAnimation.repeatCount = HUGE_VALF; // 設定無線迴圈
    
    // 動畫執行一次不銷燬, 預設為YES動畫執行一次直接銷燬
    basicAnimation.removedOnCompletion = NO;
    
    // 給圖層新增動畫 key相當於給動畫命名, 可以通過key得到該動畫
    [_basicAniLayer addAnimation:basicAnimation forKey:@"KCBasicAnimation_Rotation"];
}



/**
 *  核心動畫的執行有一個媒體時間的概念, 假設將一個旋轉動畫設定旋轉一週用時60秒的話, 那麼當動畫旋轉90度的時候, 媒體時間就是15秒,如果此時要將動畫暫停只需要讓媒體時間偏移量設定為15秒即可, 並把動畫執行速度設定為0讓它停止運動,類似的, 如果又過了60妙需要恢復動畫(此時媒體時間為75妙), 這時只要將動畫開始開始時間設定為當前媒體時間75秒減去暫停時的時間*也就是之前定格動畫時的偏移量15妙. 75-15=60秒.
    這時對暫停動畫和恢復動畫的其實是動畫速度的調整, 
    媒體時間偏移量以及恢復時的開始時間設定主要為了讓動畫更加連貫.
 
 *
 *  
 
 */

#pragma mark --- 動畫暫停
- (void)animationPause
{
    // 取得指定圖層動畫的媒體時間, 後面引數用於指定子圖層, 這裡不需要
    CFTimeInterval interval = [_basicAniLayer convertTime:CACurrentMediaTime() fromLayer:nil];
    // 設定時間偏移量, 保證暫停時停留在旋轉的位置
    [_basicAniLayer setTimeOffset:interval];
    // 速度設定為0 暫停動畫
    _basicAniLayer.speed = 0;
}

#pragma mark --- 動畫恢復
- (void)animationResume
{
    // 取得指定圖層動畫的媒體時間, 後面的引數用於指定子圖層, 這裡不需要
    CFTimeInterval interval = CACurrentMediaTime() - _basicAniLayer.timeOffset;
    // 設定時間偏移量
    _basicAniLayer.timeOffset = 0;
    // 設定開始時間
    _basicAniLayer.beginTime = interval;
    // 設定動畫速度, 開始運動
    _basicAniLayer.speed = 1.0;
}








#pragma mark ---- 動畫代理方法
// 動畫開始的時候
-(void)animationDidStart:(CAAnimation *)anim
{
    NSLog(@"animation (%@) start.\r_layer.frame=%@", anim, NSStringFromCGRect(_basicAniLayer.frame));
    // 通過前面設定的key獲得動畫
    // 設定動畫的圖層 呼叫 animationForKey:@"圖層設定的動畫Key"
    NSLog(@"%@", [_basicAniLayer animationForKey:@"KCBasicAnimation_Translation"]);
}



// 動畫結束的時候
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
    /**
     *  還有一個問題就是動畫執行完成後會重新從起始點運動到終點, 這個問題產生原因就是前面提到的,對於非根檢視, 設定圖層的可動畫屬性(在動畫結束後重新設定了position, 而position是可動畫的屬性,會產生動畫效果),解決這個問題就是在重新設定這個圖層的position關閉圖層隱式動畫
     
     *
     *  要關閉隱式動畫需要用到動畫事務CATransaction, 在事務內將隱式動畫關閉
     
        1. 開啟事務  [CATransaction begin];
        2. 禁用隱式動畫  [CATransaction setDisableActions:YES];
        3. 事務提交    [CATransaction commit];
     */
      NSLog(@"animation (%@) start.\r_layer.frame=%@", anim, NSStringFromCGRect(_basicAniLayer.frame));
    
    // 開啟事務
    [CATransaction begin];
    // 禁用隱式動畫
    [CATransaction setDisableActions:YES];
    
    _basicAniLayer.position = [[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue];
    
    CABasicAnimation *animation = (CABasicAnimation *)[anim valueForKey:@"KCBasicAnimation_Translation"];
    animation.fromValue = [NSValue valueWithCGPoint:[[anim valueForKey:@"KCBasicAnimationLocation"] CGPointValue]];;
    // 提交事務
    [CATransaction commit];
    
    // 暫停動畫
    [self animationPause];
}










#pragma mark --- 使用代理方法繪圖
- (void)drawRollImageView
{
    // 自定義圖層
    CALayer *layer = [[CALayer alloc] init];
    layer.bounds = CGRectMake(0, 0, 150, 150);
    layer.position = CGPointMake(160, 200);
    layer.backgroundColor = [UIColor redColor].CGColor;
    layer.cornerRadius = 150 / 2;
    
    // 需要裁剪圓角多餘的部分
    layer.masksToBounds = YES;
    // 如果設定多餘圖片裁剪的效果的話, 無法設定圖片的陰影
    // 如果想設定陰影效果的話, 再新增一個layer 與該圖片圖層等大, 再設定圖層
    // 下面的圖層負責繪製陰影, 上面的圖層負責顯示圖片
    
    
    // 注意 : 利用圖層形變解決影象倒立問題  沒必要走drawRect的方法
    // layer.transform = CATransform3DMakeRotation(M_PI, 1, 0, 0);
    
    // 注意 : 如果只是顯示一張圖片在圖層中, 沒必要麻煩, 直接設定圖層contents就可以了
    // 不會牽制到繪圖, 就不會涉及到倒立的問題
    
    // 設定內容
//    UIImage *image = [UIImage imageNamed:@"jiqimao"];
//    [layer setContents:(id)image.CGImage];
    
    // 設定圖層代理
    layer.delegate = self;
    // 新增圖層到根圖層
    [self.view.layer addSublayer:layer];
    
    // 呼叫圖層setNeedDisplay, 否則代理方法不會走
    [layer setNeedsDisplay];
    
    
}

// layer [layer setNeedsDisplay]的代理方法 一定要呼叫這個方法
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
    // layer 就是設定代理的那個圖層
  //  CGContextSaveGState(ctx);
    
    // 圖形上下文形變, 解決圖片倒立問題
    CGContextScaleCTM(ctx, 1, -1);
    CGContextTranslateCTM(ctx, 0, -150);
    
    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"jiqimao"]];
   // imageView.bounds = CGRectMake(0, 0, 150, 150);
    
    // 注意這個位置是相對圖層而言, 不是螢幕
    CGContextDrawImage(ctx, CGRectMake(0, 0, 150, 150),imageView.image.CGImage);
    
   // CGContextRestoreGState(ctx);
    
}



#pragma mark 繪製圓形圖層, 建立動畫
-(void)drawMyLayer{
    CGSize size=[UIScreen mainScreen].bounds.size;
    
    //獲得根圖層
    CALayer *layer=[[CALayer alloc]init];
    //設定背景顏色,由於QuartzCore是跨平臺框架,無法直接使用UIColor
    layer.backgroundColor=[UIColor colorWithRed:0 green:146/255.0 blue:1.0 alpha:1.0].CGColor;
    //設定中心點
    layer.position=CGPointMake(size.width/2, size.height/2);
    //設定大小
    layer.bounds=CGRectMake(0, 0, WIDTH,WIDTH);
    //設定圓角,當圓角半徑等於矩形的一半時看起來就是一個圓形
    layer.cornerRadius=WIDTH/2;
    //設定陰影
    layer.shadowColor=[UIColor grayColor].CGColor;
    layer.shadowOffset=CGSizeMake(2, 2);
    layer.shadowOpacity=.9;
    //設定邊框
    //    layer.borderColor=[UIColor whiteColor].CGColor;
    //    layer.borderWidth=1;
    
    //設定錨點
    //    layer.anchorPoint=CGPointZero;
    
    [self.view.layer addSublayer:layer];
}


#pragma mark --- touch方法
// 點選放大效果
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
    
    // 1. 點選移動動畫
//    UITouch *touch=[touches anyObject];
//    CALayer *layer=[self.view.layer.sublayers lastObject];
//    CGFloat width=layer.bounds.size.width;
//    if (width==WIDTH) {
//        width=WIDTH*4;
//    }else{
//        width=WIDTH;
//    }
//    layer.bounds=CGRectMake(0, 0, width, width);
//    layer.position=[touch locationInView:self.view];
//    
//    layer.cornerRadius=width/2;
    
    // 2. 基礎動畫
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self.view];
    
    
    // 判斷是否已建立過動畫, 如果已經建立則不建立動畫
    CAAnimation *animation = [_basicAniLayer animationForKey:@"KCBasicAnimation_Translation"];
    if (animation) {
        if (_basicAniLayer.speed == 0) {
            [self animationResume];
        } else {
            [self animationPause];
        }
    } else {
        // 建立並開始動畫
        [self translationAnimation:point];
        
        [self rotationAnimation];
    }
    
    
    
}



@end




</span>