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 秒,这是真实意义上的时间,里面的 startTime
和 relativeDuration
都是相对时间。以第一个为例,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
这几个选项 支持设置模式有下面几种
- 设置
fromValue
和toValue
:从fromValue
变化到toValue
- 设置
fromValue
和byValue
:从fromValue
变化到fromValue
+byValue
- 设置
byValue
和toValue
:从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
层也提供了关键帧动画的支持,CAKeyframeAnimation
和CABasicAnimation
都继承自CAPropertyAnimation
,因此她具有上面提到的那些属性,此外 CAKeyframeAnimation
还有几个特有的几个属性,
value和keyTimes
使用 values
和 keyTimes
可以共同确定一个动画的若干关键帧,示例代码如下:
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
可以用于 View
或 ViewController
直接的换场动画:
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次)来调用绘制函数,实现界面连续的不停重绘,从而实现动画效果。
示例代码
#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:动力元素行为
动画捕捉行为事例图
@property (weak, nonatomic) IBOutlet GreenView *redView;
@property(nonatomic,strong)UIDynamicAnimator *animator;
- (void)touchesBegan:(NSSet *)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 提供的一个粒子发生器系统,可以用于创建各种粒子动画,例如烟雾,焰火等效果。
例子图片
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