第二板斧就是用的最多的CoreAnimation
動畫庫,簡稱是CA,因此動畫類都是CA開頭。全部的動畫類都在 QuartzCore
庫中,在iOS7以前使用須要#import <QuartzCore/QuartzCore.h>
,iOS7以後系統已經將其自動導入了。CoreAnimation
動畫都是做用在layer
上。html
先來看下動畫類的層級關係: bash
上面講了CA動畫都是做用在Layer上,而CA動畫中修改的也是Layer的動畫屬性,能夠產生動畫的layer屬性也有Animatable
標識。app
CABasicAnimation動畫主要是設置某個動畫屬性的初始值fromValue和結束值toValue,來產生動畫效果。post
先上個示例代碼,將一個視圖往上移動一段距離: 動畫
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
動畫的時長。fromValue
和toValue
是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;
經過與代理方法結合使用,能夠用多段動畫組合成一個完整動畫。ui
CAKeyframeAnimation咱們通常稱爲關鍵幀動畫,主要是利用其values
屬性,設置多個關鍵幀屬性值,來產生動畫。url
先上一個示例代碼,將一個視圖先放大,再縮小,再放大的動畫: spa
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秒執行。values
是CAKeyframeAnimation
的屬性,設置keyPath屬性在幾個關鍵幀的值,也是id類型的。keyTimes
也是CAKeyframeAnimation
的屬性,每一個值對應相應關鍵幀的時間比例值。timingFunctions
也是CAKeyframeAnimation
的屬性,對應每一個動畫段的動畫過渡狀況;而timingFunction是CAAnimation的屬性。CAAnimationGroup的用法與其餘動畫類同樣,都是添加到layer上,比CAAnimation多了一個animations
屬性。 先看示例代碼,動畫效果是視圖一邊向上移動,一邊繞Y軸旋轉:3d
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"];
}
}
複製代碼
該動畫能夠用來實現跳起來旋轉時,修改某個圖片視圖或者按鈕的圖片等。
CATransition通常來作轉場動畫。先上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動畫。
UIBezierPath主要是用來繪製路徑的,分爲一階、二階.....n階。一階是直線,二階以上纔是曲線。而最終路徑的顯示仍是得依靠CALayer。用CoreGraphics將路徑繪製出來,最終也是繪製到CALayer上。
方法一:構造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繪製。
示例代碼以下,繪製一個右側爲弧型的視圖:
- (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];
}
複製代碼
- (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!