Core Animation 筆記記錄

奧卡姆剃鬚刀發表於2018-02-07

Core Animation

專案中實現動畫可以很好的展示我們的app 讓我們的app更具吸引力

文章目錄

  • UIView Animation
    • UIView簡單動畫
    • UIView的換場動畫
  • CALayer Animation
    • CABasicAnimation
    • CAKeyframeAnimation
    • CAAnimationGroup
    • CATransition
  • 高機動畫
    • CADisplayLink
    • UIDynamicAnimator
    • CAEmitterLayer

UIView Animation

#####簡單動畫 對於UIView上簡單的動畫 iOS提供了很方便的函式

+ (void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations
複製程式碼

第一個引數是動畫的持續時間 第一個引數是一個block 在animationBlock 對 UIView 的屬性進行調整,設定 UIView 動畫結束後最終的效果,iOS 就會自動補充中間幀,形成動畫。

可以更改的屬性有:

  • frame
  • bounds
  • center
  • transform
  • alpha
  • backgroundColor
  • contentStreth

這些屬性大都是 View 的基本屬性,下面是一個例子,這個例子中的動畫會同時改變 View 的 frame,backgroundColor 和 alpha :

[UIView animateWithDuration:2.0 animations:^{
    View.frame = CGRectMake(200, 200, 200, 200);
    View.backgroundColor = [UIColor redColor
];
    View.alpha = 0.8;
}];

複製程式碼

其中有一個比較特殊的 transform 屬性,它的型別是 CGAffineTransform,即 2D 仿射變換,這是個數學中的概念,用一個三維矩陣來表述 2D 圖形的向量變換。用 transform 屬性對 View 進行:

  • 旋轉
  • 縮放
  • 其他自定義2D變換

iOS 提供了下面的函式可以建立簡單的 2D 變換:

  • CGAffineTransformMakeScale
  • CGAffineTransformMakeRotation
  • CGAffineTransformMakeTranslation

例如下面的程式碼會將 View 縮小至原來的 1/4 大小:

[UIView animateWithDuration:2.0 animations:^{
    View.transform = CGAffineTransformMakeScale(0.5, 0.5);
}];
複製程式碼
調節引數

完整的Animation函式其實是這樣的

+ animateWithDuration:delay:options:animations:completion:
複製程式碼

可以通過delay引數調節讓動畫延遲產生,同時還一個options選項可以調節動畫進行的方式 可用options可分為兩類

控制過程

例如 UIViewAnimationOptionRepeat可以讓動畫反覆進行 UIViewAnimationOptionAllowUserInteraction 可以讓使用者允許對動畫進行過程中允許使用者互動(預設是不允許的)

控制速度

動畫的進行速度可以用速度曲線來表示 提供的選項例如 UIViewAnimationOptionCurveEaseIn 先慢後快 UIViewAnimationOptionCurveEaseOut 先快後慢

不同的選項直接可以通過“與”操作進行合併,同時使用,例如:

UIViewAnimationOptionRepeat | UIViewAnimationOptionAllowUserInteraction

關鍵幀的動畫

上面介紹的動畫中,我們只能控制開始和結束時的效果,然後由系統補全中間的過程,有些時候我們需要自己設定若干關鍵幀,實現更復雜的動畫效果,這時候就需要關鍵幀動畫的支援了。下面是一個示例:

[UIView animateKeyframesWithDuration:2.0 delay:0.0 options:UIViewKeyframeAnimationOptionRepeat | UIViewKeyframeAnimationOptionAutoreverse animations:^{
    [UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.5 animations:^{
        self.myView.frame = CGRectMake(10, 50, 100, 100);
    }];
    [UIView addKeyframeWithRelativeStartTime: 0.5 relativeDuration:0.3 animations:^{
        self.myView.frame = CGRectMake(20, 100, 100, 100);
    }];
    [UIView addKeyframeWithRelativeStartTime:0.8 relativeDuration:0.2 animations:^{
        self.myView.transform = CGAffineTransformMakeScale(0.5, 0.5);
    }];
} completion:nil];

複製程式碼

這個例子新增了三個關鍵幀,在外面的 animateKeyframesWithDuration中我們設定了持續時間為 2.0 秒,這是真實意義上的時間,裡面的 startTimerelativeDuration 都是相對時間。以第一個為例,startTime為 0.0,relativeTime為 0.5,這個動畫會直接開始,持續時間為 2.0 X 0.5 = 1.0 秒,下面第二個的開始時間是 0.5,正好承接上一個結束,第三個同理,這樣三個動畫就變成連續的動畫了。

VIew 的轉換

iOS還提供了兩個函式 用於進行兩個View之間通過動畫專場

+ transitionWithView:duration:options:animations:completion:
+ transitionFromView:toView:duration:options:completion:

複製程式碼

需要注意的是,換場動畫會在兩個View共同的父View上進行,在寫動畫之前,先要設計好VIew的繼承結構,

同樣,View之間的轉換也有很多選項可選,例如 UIViewAnimationOptionTransitionFlipFromLeft 從左邊翻轉 UIViewAnimationOptionTransitionCrossDissolve 等等

CALayer Animation

UIView 的動畫簡單易用,但是能實現的效果相對有限,上面介紹的 UIView 的幾種動畫方式,實際上是對底層 CALayer 動畫的一種封裝。直接使用 CALayer 層的動畫方法可以實現更多高階的動畫效果。 注意 使用 CALayer 動畫之前,首先需要引入 QuartzCore.framework。

CABasicAnimation

CABasicAnimation 用於建立一個CAlayer上的基本動畫

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.x"];
animation.toValue = @200;
animation.duration = 0.8;
animation.repeatCount = 5;
animation.beginTime = CACurrentMediaTime() + 0.5;
animation.fillMode = kCAFillModeRemoved;
[self.myView.layer addAnimation:animation forKey:nil];

複製程式碼

KeyPath 這裡我們使用了 animationWithKeyPath 這個方法來改變 layer 的屬性,可以使用的屬性有很多,其中很多屬性在前面介紹的 UIView 動畫部分我們也看到過,進一步驗證了 UIView 的動畫方法是對底層 CALayer 的一種封裝。

需要注意的一點是 上面我們使用position屬性 layer的這個position屬性和VIew的Frame以及Bouns的屬性都不相同,而是和Layer 的anchorPoint有關 可以由下面的公式計算得到

position.x = frame.origin.x + 0.5 * bounds.size.width;  
position.y = frame.origin.y + 0.5 * bounds.size.height; 
複製程式碼

屬性

CABasicAnimation 的屬性有下面幾個:

  • beginTime
  • duration
  • fromValue
  • toValue
  • byValue
  • repeatCount
  • autoreverses
  • timingFunction

可以看到,其中 beginTime,duration,repeatCount 等屬性和上面在 UIView 中使用到的 duration,UIViewAnimationOptionRepeat 等選項是相對應的,不過這裡的選項能夠提供更多的擴充套件性。

需要注意的是fromValue toValue byValue 這幾個選項 支援設定模式有下面幾種

  • 設定 fromValuetoValue:從 fromValue 變化到 toValue
  • 設定 fromValuebyValue:從 fromValue 變化到 fromValue + byValue
  • 設定 byValuetoValue:從 toValue - byValue 變化到 toValue
  • 設定 fromValue: 從 fromValue 變化到屬性當前值
  • 設定 toValue:從屬性當前值變化到 toValue
  • 設定 byValue:從屬性當前值變化到屬性當前值 + toValue

看起來挺複雜的 其實概括起來基本就是 如果某個值不設定 就是用這個屬性當前的值

另外 可以看到上面我們使用的

animation.toValue = @200;
複製程式碼

而不是直接使用200 因為toValue 之類的屬性是id型別 或者象這樣使用@符號,或者使用

animation.toValue = [NSNumber numberWithInt:200];

複製程式碼

最後一個比較有意思的是 timingFunction 屬性,使用這個屬性可以自定義動畫的運動曲線(節奏,pacing),系統提供了五種值可以選擇:

  • kCAMediaTimingFunctionLinear 線性動畫
  • kCAMediaTimingFunctionEaseIn 先快後慢
  • kCAMediaTimingFunctionEaseOut 先慢後快
  • kCAMediaTimingFunctionEaseInEaseOut 先慢後快再慢
  • kCAMediaTimingFunctionDefault 預設,也屬於中間比較快

此外,我們還可以使用 [CAMediaTimingFunction functionWithControlPoints] 方法來自定義運動曲線

關鍵幀動畫(CAKeyframeAnimation)

同View中類似 CAlayer層也提供了關鍵幀動畫的支援,CAKeyframeAnimationCABasicAnimation 都繼承自CAPropertyAnimation,因此她具有上面提到的那些屬性,此外 CAKeyframeAnimation 還有幾個特有的幾個屬性,

value和keyTimes 使用 valueskeyTimes 可以共同確定一個動畫的若干關鍵幀,示例程式碼如下:

CAKeyframeAnimation *anima = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"];//在這裡@"transform.rotation"==@"transform.rotation.z"
NSValue *value1 = [NSNumber numberWithFloat:-M_PI/180*4];
NSValue *value2 = [NSNumber numberWithFloat:M_PI/180*4];
NSValue *value3 = [NSNumber numberWithFloat:-M_PI/180*4];
anima.values = @[value1,value2,value3];
// anima.keyTimes = @[@0.0, @0.5, @1.0];
anima.repeatCount = MAXFLOAT;
    
[_demoView.layer addAnimation:anima forKey:@"shakeAnimation"];

複製程式碼

可以看到上面這個動畫共有三個關鍵幀,如果沒有指定 keyTimes 則各個關鍵幀會平分整個動畫的時間(duration)。

path 使用 path 屬性可以設定一個動畫的運動路徑,注意 path 只對 CALayer 的 anchorPoint 和position 屬性起作用,另外如果你設定了 path ,那麼 values 將被忽略。


    CAKeyframeAnimation *anima = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake([UIScreen mainScreen].bounds.size.width/2-100, [UIScreen mainScreen].bounds.size.height/2-100, 200, 200)];
    anima.path = path.CGPath;
    anima.duration = 2.0f;
    [_redView.layer addAnimation:anima forKey:@"pathAnimation"];


複製程式碼
組動畫(CAAnimationGroup)

組動畫可以將一組動畫組合在一起,所有動畫物件可以同時執行,示例程式碼如下:


    CAAnimationGroup *group = [[CAAnimationGroup alloc] init];
    CABasicAnimation *animationOne = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
    
    animationOne.toValue = @2.0;
    animationOne.duration = 1.0;
    
    CABasicAnimation *animationTwo = [CABasicAnimation animationWithKeyPath:@"position.x"];
    animationTwo.toValue = @400;
    animationTwo.duration = 1.0;
    [group setAnimations:@[animationOne, animationTwo]];
    [self.redView.layer addAnimation:group forKey:nil];
複製程式碼

需要注意的是,一個 group 組內的某個動畫的持續時間(duration),如果超過了整個組的動畫持續時間,那麼多出的動畫時間將不會被展示。例如一個 group 的持續時間是 5s,而組內一個動畫持續時間為 10s ,那麼這個 10s 的動畫只會展示前 5s 。

切換動畫(CATransition)

CATransition 可以用於 ViewViewController 直接的換場動畫:

    self.redView.backgroundColor = [UIColor blueColor];
    CATransition *trans = [CATransition animation];
    trans.duration = 1.0;
    trans.type = @"push";
    [self.redView.layer addAnimation:trans forKey:nil];
複製程式碼

更高階的動畫

CADisplayLink

CADisplayLink 是一個計時器物件,可以週期性的呼叫某個 selecor 方法。相比 NSTimer ,它可以讓我們以和螢幕重新整理率同步的頻率(每秒60次)來呼叫繪製函式,實現介面連續的不停重繪,從而實現動畫效果。

LLAnimation2.gif

示例程式碼

#import "GreenView.h"



@implementation GreenView

- (void)startAnimationFrom:(CGFloat)from To:(CGFloat)to
{
    self.from = from;
    self.to = to;
    if (self.displayLink == nil) {
        self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];
        [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop]
                               forMode:NSDefaultRunLoopMode];
    }
}

// 重複呼叫這個方法以重繪整個 View
- (void)tick:(CADisplayLink *)displayLink
{
    [self setNeedsDisplay];
}


- (void)endAnimation
{
    
    [self.displayLink invalidate];
    self.displayLink = nil;
}



- (void)drawRect:(CGRect)rect
{
    CALayer *layer = self.layer.presentationLayer;
    CGFloat progress = 1 - (layer.position.y - self.to) / (self.from - self.to);
    CGFloat height = CGRectGetHeight(rect);
    CGFloat deltaHeight = height / 2 * (0.5 - fabs(progress - 0.5));
    CGPoint topLeft = CGPointMake(0, deltaHeight);
    CGPoint topRight = CGPointMake(CGRectGetWidth(rect), deltaHeight);
    CGPoint bottomLeft = CGPointMake(0, height);
    CGPoint bottomRight = CGPointMake(CGRectGetWidth(rect), height);
    UIBezierPath* path = [UIBezierPath bezierPath];
    [[UIColor blueColor] setFill];
    [path moveToPoint:topLeft];
    [path addQuadCurveToPoint:topRight controlPoint:CGPointMake(CGRectGetMidX(rect), 0)];
    [path addLineToPoint:bottomRight];
    [path addQuadCurveToPoint:bottomLeft controlPoint:CGPointMake(CGRectGetMidX(rect), height - deltaHeight)];
    [path closePath];
    [path fill];
}



外界呼叫
    CGFloat from = CGRectGetHeight(self.view.bounds) - CGRectGetHeight(self.redView.bounds) / 2;
    CGFloat to = 100;    
    self.redView.center = CGPointMake(self.redView.center.x, from);    
    [self.redView startAnimationFrom:from To:to];    
    [UIView animateWithDuration:1 delay:0 usingSpringWithDamping:0.85 initialSpringVelocity:0 options:0 animations:^{        
        self.redView.center = CGPointMake(self.redView.center.x, to);        
    } completion:^(BOOL finished) {        
        [self.redView endAnimation];        
    }];
複製程式碼
UIDynamicAnimator(物理模擬動畫)

UIDynamicAnimator 是 iOS 7 引入的一個新類,可以建立出具有物理模擬效果的動畫,具體提供了下面幾種物理模擬行為:

  • UIGravityBehavior:重力行為
  • UICollisionBehavior:碰撞行為
  • UISnapBehavior:捕捉行為
  • UIPushBehavior:推動行為
  • UIAttachmentBehavior:附著行為
  • UIDynamicItemBehavior:動力元素行為

動畫捕捉行為事例圖

LLAnimation.gif

@property (weak, nonatomic) IBOutlet GreenView *redView;
@property(nonatomic,strong)UIDynamicAnimator *animator;

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    UITouch *touch = [touches anyObject];
    CGPoint point = [touch locationInView:touch.view];
    
    UISnapBehavior *snap = [[UISnapBehavior alloc]initWithItem:self.redView snapToPoint:point];
    
    //減震
    snap.damping = 0.5;
    [self.animator removeAllBehaviors];//移除之前所有行為
    [self.animator addBehavior:snap];//新增新的行為   
}

複製程式碼
CAEmitterLayer

CAEmitterLayer 是 Core Animation 提供的一個粒子發生器系統,可以用於建立各種粒子動畫,例如煙霧,焰火等效果。

例子圖片

LLAnimation3.gif


    CAEmitterLayer *emitterLayer = [CAEmitterLayer layer]; 
    emitterLayer.emitterPosition = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.origin.y); 
    emitterLayer.emitterZPosition = 10; 
    emitterLayer.emitterSize = CGSizeMake(self.view.bounds.size.width, 0); 
    emitterLayer.emitterShape = kCAEmitterLayerSphere; 
    
    CAEmitterCell *emitterCell = [CAEmitterCell emitterCell]; 
    emitterCell.scale = 0.1; 
    emitterCell.scaleRange = 0.2; 
    emitterCell.emissionRange = (CGFloat)M_PI_2; 
    emitterCell.lifetime = 5.0; 
    emitterCell.birthRate = 10; 
    emitterCell.velocity = 200; 
    emitterCell.velocityRange = 50; 
    emitterCell.yAcceleration = 250; 
    
    emitterCell.contents = (id)[[UIImage imageNamed:@"Ball_blue"] CGImage]; 
 
複製程式碼

動畫Demo集合 AnimDemo

相關文章