iOS 動畫篇(一) Core Animation


  iOS中實現動畫有兩種方式,一種是自己不斷的通過drawRect:方法來繪制,另外一種就是使用核心動畫(Core Animation)。

  導語:

  核心動畫提供高幀速率和流暢的動畫,而不會增加CPU的負擔和減慢你的應用程序。換句話說,使用核心動畫你就不用擔心性能的事了。同時,使用核心動畫只需要提供少數參數,使用起來很簡單。需要注意的是核心動畫針對的是CALayer而不是UIView,所以對使用核心動畫前,需要先對CALayer的知識有過了解。

  一、系統層級介紹

  如圖所示,核心動畫位於AppKit和UIKit之下,並緊密集成到Cocoa和Cocoa Touch的視圖工作流中。

  

  二、核心動畫類圖介紹

  先來一張類圖(ps:這類圖是盜的,等我會畫的時候就自己畫了)。如圖所示,CAAnimation作為虛基類實現了CAMediaTiming協議(其實還實現了CAAction協議)。CAAnimation有三個子類CAAnimationGroup(組動畫)、CAPropertyAnimation(屬性動畫)、CATrasition(漸變動畫)。CAAnimation不能直接使用,應該使用它的子類。作為CAPropertyAnimation也有兩個子類CABasicAnimation(基礎動畫)、CAKeyFrameAnimation(關鍵幀動畫)。CAPropertyAnimation也不能直接使用,應該使用兩個子類。綜上所訴要使用核心動畫,可以使用的就是以下四個類(CAAnimationGroup、CATrasition、CABasicAnimation、CAKeyFrameAnimation)。

  PS:在iOS9.0+以后,核心動畫又加入了CASpringAnimation(彈性動畫),CASpringAnimation繼承自CABasicAnimation。

  

  三、基本使用示例

  在這里先介紹一下代碼統一的代碼,所有的示例代碼均繼承父類創建TestLayer代碼

self.testLayer = ({
        CALayer *tempLayer = [CALayer new];
        tempLayer.backgroundColor = [UIColor cyanColor].CGColor;
        tempLayer.position = self.view.center;
        tempLayer.bounds = CGRectMake(0, 0, 100, 100);
        [self.view.layer addSublayer:tempLayer];
        tempLayer;
    });

 

  首先要介紹CAPropertyAnimation(屬性動畫),屬性動畫創建的時候需要指定keyPath,可以動畫的屬性可以在官網文檔查看

  3.1 CABasicAnimation

  使用代碼示例

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:self.view];
    CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
    positionAnimation.fromValue = [NSValue valueWithCGPoint:self.testLayer.presentationLayer.position];
    positionAnimation.toValue = [NSValue valueWithCGPoint:point];
    positionAnimation.duration = 1.f;//動畫時長
    positionAnimation.removedOnCompletion = NO;//是否在完成時移除
    positionAnimation.fillMode = kCAFillModeForwards;//動畫結束后是否保持狀態
    [self.testLayer addAnimation:positionAnimation forKey:@"positionAnimation"];
}

 

  上面的示例代碼通過使用CABasicAnimation來實現了位置動畫,讓testLayer每次動畫移動到點擊的位置,在使用基礎動畫的時候需要指定三個屬性:fromValue(可省略,默認值為動畫的keyPath對應的當前屬性值),toValue,duration(默認值為0.25s)。

  運行效果如下:

  

  3.2 隱式動畫

  如果對於基礎動畫,不需要設置其他值,僅僅想要設置toValue來實現動畫的話,那么就可以使用隱式動畫。隱式動畫其實通過直接修改layer的動畫屬性,系統會按照基礎動畫的默認值來實現動畫。代碼如下

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    self.testLayer.position = [touch locationInView:self.view];//修改位置的隱式動畫
    CGFloat WH = arc4random_uniform(100);
    if (WH < 20) {
        WH += 50;
    }
    self.testLayer.bounds = CGRectMake(0, 0, WH, WH);
    UIColor *color = [UIColor colorWithRed:arc4random_uniform(255) / 255.0 green:arc4random_uniform(255) / 255.0 blue:arc4random_uniform(255) / 255.0 alpha:1.f];
    self.testLayer.backgroundColor = color.CGColor;//修改背景色的隱式動畫
}

  效果如下

  

  

  3.3 CAKeyFrameAnimation

  關鍵幀動畫的使用中可以設置path,也可以設置values,在設置values的時候,默認會把動畫時間按照values的個數進行平均分配。下面是使用path來做的動畫,同時顯示了軌跡。

  先上效果:

  

  代碼如下:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"CAKeyframeAnimation");
    //重新設置初始位置
    self.testLayer.position = CGPointMake(33.5, 409.5);
    self.testLayer.bounds = CGRectMake(0, 0, 50, 50);
    
    CGPathRef bezirePath = [self bezirePath];
    
    //繪制軌跡
    CAShapeLayer *positionTrackLayer = [[CAShapeLayer alloc] init];
    
    positionTrackLayer.path = bezirePath;
    positionTrackLayer.strokeColor = [UIColor redColor].CGColor;
    positionTrackLayer.fillColor = [UIColor clearColor].CGColor;
    [self.view.layer addSublayer:positionTrackLayer];
    
    //添加保存動畫
    self.positionAnimation = [self keyframeAnimation:bezirePath];
}

- (CGPathRef)bezirePath
{
    UIBezierPath* bezierPath = UIBezierPath.bezierPath;
    CGPoint fromPoint = CGPointMake(33.5, 409.5);
    [bezierPath moveToPoint: fromPoint];
    [bezierPath addCurveToPoint: CGPointMake(127.5, 119.78) controlPoint1: CGPointMake(58.5, 433.93) controlPoint2: CGPointMake(86.5, 95.16)];
    [bezierPath addCurveToPoint: CGPointMake(183.5, 554.5) controlPoint1: CGPointMake(168.5, 144.4) controlPoint2: CGPointMake(112.5, 557.05)];
    [bezierPath addCurveToPoint: CGPointMake(251.5, 119.78) controlPoint1: CGPointMake(254.5, 551.95) controlPoint2: CGPointMake(251.5, 119.78)];
    return bezierPath.CGPath;
}

- (CAKeyframeAnimation *)keyframeAnimation:(CGPathRef)bezirePath
{
    CAKeyframeAnimation *moveAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    
    moveAnimation.path = bezirePath;
    moveAnimation.fillMode = kCAFillModeForwards;
    moveAnimation.removedOnCompletion = NO;
    moveAnimation.duration = 3.f;
    
    return moveAnimation;
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [self.testLayer addAnimation:self.positionAnimation forKey:@"position"];
}

  3.4 AnimationGroup(組動畫)

  組動畫的作用是可以把多個動畫組合在一起比如移動、旋轉、縮放、透明度等等。在使用動畫組時需要先將需要組合的動畫創建好,最后放到CAAnimationGroup的animations數組中即可,animationGroup其他設置與基礎動畫差不多

  先來一個類似於漂浮氣泡的動畫

  

  這個動畫的實現實際上就是使用組動畫來同時改變layer在x,y軸上的縮放、x,y軸上的移動來實現的,以下為實現代碼:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"CAAnimationGroup");
    
    //重新設置layer大小與圓角
    self.testLayer.bounds = CGRectMake(0, 0, 30, 30);
    self.testLayer.cornerRadius = 30 / 2;
    
    //設置x軸方向的縮放動畫
    CAKeyframeAnimation *xScaleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale.x"];
    xScaleAnimation.values = @[@1, @0.9, @1, @1.1, @0.9, @1];
    xScaleAnimation.duration = 3.f;
    xScaleAnimation.repeatCount = CGFLOAT_MAX;
    xScaleAnimation.removedOnCompletion = NO;
    xScaleAnimation.fillMode = kCAFillModeForwards;
    
    //設置y軸方向的縮放動畫
    CAKeyframeAnimation *yScaleAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.scale.x"];
    yScaleAnimation.values = @[@0.9, @1, @1.1, @0.8, @1, @0.9];
    yScaleAnimation.duration = 3.f;
    yScaleAnimation.repeatCount = CGFLOAT_MAX;
    yScaleAnimation.removedOnCompletion = NO;
    yScaleAnimation.fillMode = kCAFillModeForwards;
    
    //設置x軸方向的移動動畫
    CAKeyframeAnimation *xTranslationAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.x"];
    xTranslationAnimation.values = @[@0, @5, @(-5), @0, @5, @0];
    xTranslationAnimation.duration = 3.f;
    xTranslationAnimation.repeatCount = CGFLOAT_MAX;
    xTranslationAnimation.removedOnCompletion = NO;
    xTranslationAnimation.fillMode = kCAFillModeForwards;
    
    //設置y軸方向的移動動畫
    CAKeyframeAnimation *yTranslationAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform.translation.y"];
    yTranslationAnimation.values = @[@0, @5, @1, @-5, @0];
    yTranslationAnimation.duration = 3.f;
    yTranslationAnimation.repeatCount = CGFLOAT_MAX;
    yTranslationAnimation.removedOnCompletion = NO;
    yTranslationAnimation.fillMode = kCAFillModeForwards;
    
    //組動畫
    CAAnimationGroup *groupAnimation = [[CAAnimationGroup alloc] init];
    groupAnimation.animations = @[xScaleAnimation, yScaleAnimation, xTranslationAnimation, yTranslationAnimation];//將所有動畫添加到動畫組
    groupAnimation.duration = 3.f;
    groupAnimation.repeatCount = CGFLOAT_MAX;
    groupAnimation.removedOnCompletion = NO;
    groupAnimation.fillMode = kCAFillModeForwards;
    
    [self.testLayer addAnimation:groupAnimation forKey:@"groupAnimation"];
}

  3.5 CATransition(過渡動畫)

  過渡動畫的使用方式和屬性動畫就不同了,還是先來效果

  對於具體的解釋見代碼:

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    CATransition *transition = [CATransition animation];
    transition.startProgress = 0;//開始進度
    transition.endProgress = 1;//結束進度
    transition.type = kCATransitionReveal;//過渡類型
    transition.subtype = kCATransitionFromLeft;//過渡方向
    transition.duration = 1.f;
    
    UIColor *color = [UIColor colorWithRed:arc4random_uniform(255) / 255.0 green:arc4random_uniform(255) / 255.0 blue:arc4random_uniform(255) / 255.0 alpha:1.f];
    self.testLayer.backgroundColor = color.CGColor;
    
    [self.testLayer addAnimation:transition forKey:@"transition"];
}

  以上就是核心動畫的基本使用了

  接下來介紹核心動畫的進階使用

    四、核心動畫進階使用

  核心動畫允許我們在動畫過程中進行操作,如:暫停、恢復、移除

  4.1 動畫的暫停與恢復

  暫停動畫需要做兩步操作:

  1. 利用layer的timeOffset來記錄當前暫停的時間點

  2. 設置layer的speed為0

  恢復動畫需要做以下操作:

  1. 取出暫停時間的時間點

  2. 恢復speed為1

  3. 設置timeOffset為0

  4. 設置beginTimer為0

  5. 計算當前時間與暫停時間點的時間差

  6. 將beginTimer設置為計算出來的時間差

  還是先來一個暫停效果:

  

  具體的暫停與恢復的代碼如下:

@interface ViewController6 ()

@property (nonatomic, strong) CAKeyframeAnimation *positionAnimation;
@property (nonatomic, assign) BOOL isPositionAnimation;

@end

@implementation ViewController6

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"動畫的暫停與恢復");
    //重新設置初始位置
    self.testLayer.position = CGPointMake(33.5, 409.5);
    self.testLayer.bounds = CGRectMake(0, 0, 50, 50);
    
    CGPathRef bezirePath = [self bezirePath];
    
    //繪制軌跡
    CAShapeLayer *positionTrackLayer = [[CAShapeLayer alloc] init];
    
    positionTrackLayer.path = bezirePath;
    positionTrackLayer.strokeColor = [UIColor redColor].CGColor;
    positionTrackLayer.fillColor = [UIColor clearColor].CGColor;
    [self.view.layer addSublayer:positionTrackLayer];
    
    //添加保存動畫
    self.positionAnimation = [self keyframeAnimation:bezirePath];
}

- (CGPathRef)bezirePath
{
    UIBezierPath* bezierPath = UIBezierPath.bezierPath;
    CGPoint fromPoint = CGPointMake(33.5, 409.5);
    [bezierPath moveToPoint: fromPoint];
    [bezierPath addCurveToPoint: CGPointMake(127.5, 119.78) controlPoint1: CGPointMake(58.5, 433.93) controlPoint2: CGPointMake(86.5, 95.16)];
    [bezierPath addCurveToPoint: CGPointMake(183.5, 554.5) controlPoint1: CGPointMake(168.5, 144.4) controlPoint2: CGPointMake(112.5, 557.05)];
    [bezierPath addCurveToPoint: CGPointMake(251.5, 119.78) controlPoint1: CGPointMake(254.5, 551.95) controlPoint2: CGPointMake(251.5, 119.78)];
    return bezierPath.CGPath;
}

- (CAKeyframeAnimation *)keyframeAnimation:(CGPathRef)bezirePath
{
    CAKeyframeAnimation *moveAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    
    moveAnimation.path = bezirePath;
    moveAnimation.fillMode = kCAFillModeForwards;
    moveAnimation.removedOnCompletion = NO;
    moveAnimation.duration = 3.f;
    
    return moveAnimation;
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    if ([self.testLayer animationForKey:[self.testLayer.animationKeys firstObject]] == nil) {
        [self.testLayer addAnimation:self.positionAnimation forKey:@"position"];
        
        self.isPositionAnimation = YES;
        return;
    }
    
    if (self.isPositionAnimation) {
        [self pauseLayer:self.testLayer];
        self.isPositionAnimation = NO;
    }else{
        self.isPositionAnimation = YES;
        [self resumeLayer:self.testLayer];
    }
}

#pragma mark - 動畫的暫停與恢復

/**
 暫停動畫

 @param layer 要暫停的layer
 */
-(void)pauseLayer:(CALayer *)layer {
    CFTimeInterval pausedTime = [layer convertTime:CACurrentMediaTime() fromLayer:nil];
    layer.speed = 0.0;
    layer.timeOffset = pausedTime;
}

/**
 恢復動畫

 @param layer 要恢復的layer
 */
-(void)resumeLayer:(CALayer *)layer {
    CFTimeInterval pausedTime = [layer timeOffset];
    layer.speed = 1.0;
    layer.timeOffset = 0.0;
    layer.beginTime = 0.0;
    CFTimeInterval timeSincePause = [layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
    layer.beginTime = timeSincePause;
}

 

  4.2 監聽動畫的完成狀態

  有時候我們需要監聽動畫的開始與完成,這就要靠設置動畫的delegate來實現了

 

@protocol CAAnimationDelegate <NSObject>
@optional

//在動畫開始時被調用
- (void)animationDidStart:(CAAnimation *)anim;

//在動畫結束時被調用, 如果是動畫被移除則flag為false,正常結束為true
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;

@end

  示例代碼如下:

  需要注意的是

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"監聽動畫完成狀態");
    CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
    CGPoint fromPoint = CGPointMake(100, 100);
    positionAnimation.fromValue = [NSValue valueWithCGPoint:fromPoint];
    positionAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(300, 300)];
    positionAnimation.duration = 3.f;
    positionAnimation.fillMode = kCAFillModeForwards;
    positionAnimation.removedOnCompletion = NO;
    
    positionAnimation.delegate = self;
    
    [positionAnimation setValue:@"位置動畫" forKey:@"animationName"];//添加屬性名
    
    [self.testLayer addAnimation:positionAnimation forKey:@"positionAnimation"];
}


//在動畫開始時被調用
- (void)animationDidStart:(CAAnimation *)anim
{
    NSLog(@"%@-----動畫開始", [anim valueForKey:@"animationName"]);
}

//在動畫結束時被調用, 如果是動畫被移除則flag為false,正常結束為true
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
    if (flag) {
        NSLog(@"%@-----動畫正常結束", [anim valueForKey:@"animationName"]);
    }else{
        NSLog(@"%@-----動畫移除結束", [anim valueForKey:@"animationName"]);
    }
}

  4.2  CATransaction(事務)的使用

    4.2.1 關閉隱式動畫

  我們可以利用事務來關閉隱式動畫,使用方式如下,關閉隱式動畫之后在修改layer的屬性就不會在觸發動畫了。

  

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    [CATransaction begin];
    [CATransaction setDisableActions:YES];//關閉動畫行為
    
    UITouch *touch = [touches anyObject];
    self.testLayer.position = [touch locationInView:self.view];//修改位置的隱式動畫
    CGFloat WH = arc4random_uniform(100);
    if (WH < 20) {
        WH += 50;
    }
    self.testLayer.bounds = CGRectMake(0, 0, WH, WH);
    [CATransaction commit];
}

效果:

  

  4.2.1 利用事務統一設置動畫參數

  可以統一設置的參數有以下四種

kCATransactionAnimationDuration //動畫時長
kCATransactionDisableActions //關閉動畫行為
kCATransactionAnimationTimingFunction //動畫時間曲線
kCATransactionCompletionBlock //動畫完成block

  我們以統一設置動畫時長為例,以下代碼統一設置動畫時長為5s

  

[CATransaction begin];
    
    [CATransaction setValue:[NSNumber numberWithFloat:5.f]
                     forKey:kCATransactionAnimationDuration];
    
    CABasicAnimation *positionAnimation = [CABasicAnimation animationWithKeyPath:@"position"];
    CGPoint fromPoint = CGPointMake(100, 100);
    positionAnimation.fromValue = [NSValue valueWithCGPoint:fromPoint];
    positionAnimation.toValue = [NSValue valueWithCGPoint:CGPointMake(300, 300)];
    positionAnimation.fillMode = kCAFillModeForwards;
    positionAnimation.removedOnCompletion = NO;
    
    [positionAnimation setValue:@"位置動畫" forKey:@"animationName"];//添加屬性名
    
    [self.testLayer addAnimation:positionAnimation forKey:@"positionAnimation"];
    
    [CATransaction commit];

以上就是Core Animation的基本使用了,你可以在這里下載代碼

本文個人原創,轉載請注明出處 (http://www.cnblogs.com/pretty-guy/p/8259657.html)

下一篇文章講Core Animation 與 CAShapeLayer組合起來使用

       

 

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM