iOS動畫三板斧(二)--CoreAnimation動畫

介紹

第二板斧就是用的最多的CoreAnimation動畫庫,簡稱是CA,因此動畫類都是CA開頭。全部的動畫類都在 QuartzCore 庫中,在iOS7以前使用須要#import <QuartzCore/QuartzCore.h>,iOS7以後系統已經將其自動導入了。CoreAnimation動畫都是做用在layer上。html

先來看下動畫類的層級關係: bash

動畫層級結構.png
關於上圖中的層級結構只須要了解一下,用的多了,天然就記住了。本篇只講述CABasicAnimation、CAKeyframeAnimation、CAAnimationGroup的使用。

使用

上面講了CA動畫都是做用在Layer上,而CA動畫中修改的也是Layer的動畫屬性,能夠產生動畫的layer屬性也有Animatable標識。app

1.CABasicAnimation

CABasicAnimation動畫主要是設置某個動畫屬性的初始值fromValue和結束值toValue,來產生動畫效果。post

先上個示例代碼,將一個視圖往上移動一段距離: 動畫

animation.gif

    CABasicAnimation *postionAnimation = [CABasicAnimation animationWithKeyPath:@"position.y"];
    postionAnimation.duration = 1.0f;
    postionAnimation.fromValue = @(self.squareView.center.y);
    postionAnimation.toValue = @(self.squareView.center.y - 300);
    postionAnimation.removedOnCompletion = NO;
    postionAnimation.delegate = self;
    postionAnimation.autoreverses = YES;
    postionAnimation.fillMode = kCAFillModeForwards;
    postionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    [self.squareView.layer addAnimation:postionAnimation forKey:@"posstionAnimation"];
複製代碼
  • 動畫的建立使用animationWithKeyPath:,由於使用的keyPath因此動畫屬性或者其結構體中元素均可以產生動畫。
  • duration 動畫的時長。
  • fromValuetoValue 是CABasicAnimation的屬性,都是id類型的,因此要將基本類型包裝成對象。
  • removedOnCompletion 決定動畫執行完以後是否將該動畫的影響移除,默認是YES,則layer回到動畫前的狀態。
  • fillMode 是個枚舉值(四種),當removedOnCompletion設置爲NO以後纔會起做用。能夠設置layer是保持動畫開始前的狀態仍是動畫結束後的狀態,或是其餘的。
  • autoreverses 表示動畫結束後是否 backwards(回退) 到動畫開始前的狀態。可與上面兩個屬性組合出不一樣效果。
  • timingFunction 動畫的運動是勻速線性的仍是先快後慢等,相似UIView動畫的opitions。另外,CAMediaTimingFunction 方法能夠自定義。
  • delegate 代理,兩個動畫代理方法:- (void)animationDidStart:(CAAnimation *)anim;- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag;
  • - (void)addAnimation:(CAAnimation *)anim forKey:(nullable NSString *)key; 給某個layer添加動畫,與之對應的移除某個動畫是- (void)removeAnimationForKey:(NSString *)key;
  • 還有一些其餘的屬性,就不一一介紹了,能夠在使用的使用去.h文件中查看。

經過與代理方法結合使用,能夠用多段動畫組合成一個完整動畫。ui

2.CAKeyframeAnimation

CAKeyframeAnimation咱們通常稱爲關鍵幀動畫,主要是利用其values屬性,設置多個關鍵幀屬性值,來產生動畫。url

先上一個示例代碼,將一個視圖先放大,再縮小,再放大的動畫: spa

animation.gif

    CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
    keyAnimation.duration = 1.0f;
    keyAnimation.beginTime = CACurrentMediaTime() + 1.0;
    
    CATransform3D transform1 = CATransform3DMakeScale(1.5, 1.5, 0);
    CATransform3D transform2 = CATransform3DMakeScale(0.8, 0.8, 0);
    CATransform3D transform3 = CATransform3DMakeScale(3, 3, 0);
    
    keyAnimation.values = @[[NSValue valueWithCATransform3D:transform1],[NSValue valueWithCATransform3D:transform2],[NSValue valueWithCATransform3D:transform3]];
    keyAnimation.keyTimes = @[@0,@0.5,@1];
    keyAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    keyAnimation.removedOnCompletion = NO;
    keyAnimation.fillMode = kCAFillModeForwards;
    [_someView.layer addAnimation:keyAnimation forKey:nil];
複製代碼
  • beginTime 也是CAAnimation類的屬性,能夠設置動畫延遲多久執行,示例代碼是延遲1秒執行。
  • valuesCAKeyframeAnimation的屬性,設置keyPath屬性在幾個關鍵幀的值,也是id類型的。
  • keyTimes 也是CAKeyframeAnimation的屬性,每一個值對應相應關鍵幀的時間比例值。
  • timingFunctions 也是CAKeyframeAnimation的屬性,對應每一個動畫段的動畫過渡狀況;而timingFunction是CAAnimation的屬性。

3.CAAnimationGroup

CAAnimationGroup的用法與其餘動畫類同樣,都是添加到layer上,比CAAnimation多了一個animations屬性。 先看示例代碼,動畫效果是視圖一邊向上移動,一邊繞Y軸旋轉:3d

animation.gif

    CABasicAnimation *rotationYAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    rotationYAnimation.fromValue = @0;
    rotationYAnimation.toValue = @(M_PI);
    rotationYAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    
    CABasicAnimation *postionAnimation = [CABasicAnimation animationWithKeyPath:@"position.y"];
    postionAnimation.fromValue = @(_markView.center.y);
    postionAnimation.toValue = @(_markView.center.y - 100);
    postionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    
    CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
    animationGroup.duration = kUpDuration;
    animationGroup.removedOnCompletion = NO;
    animationGroup.fillMode = kCAFillModeForwards;
    animationGroup.delegate = self;
    animationGroup.animations = @[rotationYAnimation, postionAnimation];
    
    [_markView.layer addAnimation:animationGroup forKey:kJumpAnimation];
複製代碼

CAAnimationGroup的animations中能夠放其餘任何動畫類(包括CAAnimationGroup),須要注意的是animations裏的動畫設置了duration以後動畫可能會有不一樣,通常裏面不設置,在最外層設置group的duration便可。代理

接上面示例以後的動畫,實現視圖繼續繞Y軸旋轉90°,下落回原處:

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
    if ([anim isEqual:[_someView.layer animationForKey:@"jumpUp"]]) {
        CABasicAnimation *rotationYAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
        rotationYAnimation.fromValue = @(M_PI_2);
        rotationYAnimation.toValue = @(M_PI);
        rotationYAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        
        CABasicAnimation *postionAnimation = [CABasicAnimation animationWithKeyPath:@"position.y"];
        postionAnimation.fromValue = @(_someView.center.y - 14);
        postionAnimation.toValue = @(_someView.center.y);
        postionAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
        
        CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
        animationGroup.duration = 0.256;
        animationGroup.removedOnCompletion = NO;
        animationGroup.fillMode = kCAFillModeForwards;
        animationGroup.delegate = self;
        animationGroup.animations = @[rotationYAnimation, postionAnimation];
        
        [_someView.layer addAnimation:animationGroup forKey:@"jumpDown"];
    }
}
複製代碼

該動畫能夠用來實現跳起來旋轉時,修改某個圖片視圖或者按鈕的圖片等。

4.CATransition(2016-04-22補充)

CATransition通常來作轉場動畫。先上gif動畫效果

transition動畫.gif

//修改視圖的背景色
    _someView.backgroundColor = [UIColor greenColor];
    CATransition *animation = [CATransition animation];
    animation.duration = 0.5;
    /* 這裏可設置的參數有:kCATransitionFade、kCATransitionPush、kCATransitionReveal、kCATransitionMoveIn、
    "cube""suckEffect""oglFlip""rippleEffect""pageCurl""pageUnCurl""cameraIrisHollowOpen"、
     "cameraIrisHollowClose",這些都是動畫類型
    */
    animation.type = @"cube";
    // 動畫執行的方向,kCATransitionFromRight、kCATransitionFromLeft、kCATransitionFromTop、kCATransitionFromBottom
    animation.subtype = kCATransitionFromRight;
    animation.timingFunction = UIViewAnimationOptionCurveEaseInOut;
    [_someView.layer addAnimation:animation forKey:nil];
    //也能夠寫這裏
//    _someView.backgroundColor = [UIColor greenColor];
複製代碼

只須要在動畫開始前或者動畫開始後替換掉視圖上顯示的內容便可。 示例代碼可能與gif圖不太一致,由於gif圖是從其餘demo中錄製下來的。

gif圖來自青玉伏案的demo:他的文章有更詳細的demo講解,地址在這裏

附加

附加的內容是關於CALayer和UIBezierPath。我的以爲理解了UIBezierPath和CALayer,才能更好的理解CoreAnimation動畫。

1.UIBezierPath

UIBezierPath主要是用來繪製路徑的,分爲一階、二階.....n階。一階是直線,二階以上纔是曲線。而最終路徑的顯示仍是得依靠CALayer。用CoreGraphics將路徑繪製出來,最終也是繪製到CALayer上。

貝塞爾曲線.png

方法一:構造bezierPath對象,通常用於自定義路徑。

方法二:繪製圓弧路徑,參數1是中心點位置,參數2是半徑,參數3是開始的弧度值,參數4是結束的弧度值,參數5是是否順時針(YES是順時針方向,NO逆時針)。

方法三:根據某個路徑繪製路徑。

方法四:根據某個CGRect繪製內切圓或橢圓(CGRect是正方形即爲圓,爲長方形則爲橢圓)。

方法五:根據某個CGRect繪製路徑。

方法六:繪製帶圓角的矩形路徑,參數2哪一個角,參數3,橫、縱向半徑。

方法七:繪製每一個角都是圓角的矩形,參數2是半徑。

自定義路徑時經常使用的API:

- (void)moveToPoint:(CGPoint)point; // 移到某個點
- (void)addLineToPoint:(CGPoint)point; // 繪製直線
- (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2; //繪製貝塞爾曲線
- (void)addQuadCurveToPoint:(CGPoint)endPoint controlPoint:(CGPoint)controlPoint; // 繪製規則的貝塞爾曲線
- (void)addArcWithCenter:(CGPoint)center radius:(CGFloat)radius startAngle:(CGFloat)startAngle endAngle:(CGFloat)endAngle clockwise:(BOOL)clockwise
// 繪製圓形曲線
- (void)appendPath:(UIBezierPath *)bezierPath; // 拼接曲線
複製代碼

若是將路徑顯示的圖案顯示到視圖上呢?

有三種方式:

一、直接使用UIBezierPath的方法;

二、使用CoreGraphics繪製;

三、利用CAShapeLayer繪製。

示例代碼以下,繪製一個右側爲弧型的視圖:

animation.gif

- (void)drawRect:(CGRect)rect
{
    UIColor *fillColor = [UIColor colorWithRed:0.0 green:0.722 blue:1.0 alpha:1.0];
    
    UIBezierPath *bezierPath = [UIBezierPath bezierPath];
    [bezierPath moveToPoint:CGPointMake(0, 0)];
    [bezierPath addLineToPoint:CGPointMake(rect.size.width - spaceWidth, 0)];
    [bezierPath addQuadCurveToPoint:CGPointMake(rect.size.width - spaceWidth, rect.size.height) controlPoint:CGPointMake(rect.size.width - spaceWidth + _deltaWith, rect.size.height * 0.5)];
    [bezierPath addLineToPoint:CGPointMake(0, rect.size.height)];
    [bezierPath addLineToPoint:CGPointMake(0, 0)];
    [bezierPath closePath];
    
    // 一、bezierPath方法
//    [fillColor setFill];
//    [bezierPath fill];
    
    // 二、使用CoreGraphics
//    CGContextRef ctx = UIGraphicsGetCurrentContext();
//    CGContextAddPath(ctx, bezierPath.CGPath);
//    CGContextSetFillColorWithColor(ctx, fillColor.CGColor);
//    CGContextFillPath(ctx);
    
    // 3.CAShaperLayer
    [self.layer.sublayers makeObjectsPerformSelector:@selector(removeFromSuperlayer)];
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.path = bezierPath.CGPath;
    shapeLayer.fillColor = fillColor.CGColor;
    [self.layer addSublayer:shapeLayer];
}
複製代碼

進度條.gif
上圖這樣的視圖是用UIBezierPath用多個CAShapeLayer製做出來的,而動畫效果只須要改變進度的layer的strokeEnd和修改下面表明水面進度的視圖位置便可。動畫的組合也能夠有多種方式組合 動畫的示例代碼:

- (void)setProgress:(CGFloat)progress animated:(BOOL)animated duration:(NSTimeInterval)duration
{
    CGFloat tempPro = progress;
    if (tempPro > 1.0) {
        tempPro = 1.0;
    } else if (progress < 0.0){
        tempPro = 0.0;
    }
    _progress = tempPro;
    
    CABasicAnimation *pathAniamtion = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    pathAniamtion.duration = duration;
    pathAniamtion.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
    pathAniamtion.fromValue = [NSNumber numberWithFloat:0.0f];
    pathAniamtion.toValue = [NSNumber numberWithFloat:_progress];
    pathAniamtion.autoreverses = NO;
    [_progressLayer addAnimation:pathAniamtion forKey:nil];
    
    // 水位上升的動畫
    if (!_showSolidAnimation) {
        return;
    }
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        _imageView.transform = CGAffineTransformIdentity;
        [UIView animateWithDuration:duration animations:^{
            CGRect rect = _imageView.frame;
            CGFloat dy = rect.size.height * progress;
            _imageView.transform = CGAffineTransformMakeTranslation(0, -dy);
        }];
    });
}
複製代碼

在用自定義的CAShapeLayer作動畫時,建議在動畫開始前先將動畫屬性與最終的屬性值一致,再開始動畫,不要使用removedOnCompletion控制最終的狀態,這在WWDC蘋果這麼建議。

另一個酷炫動畫的實現都是多種簡單動畫的組合! 關於CoreAnimation動畫就先介紹這麼多吧,Have fun!

相關文章
相關標籤/搜索