iOS 核心动画 Core Animation浅谈

代码地址以下:
http://www.demodashi.com/demo/11603.htmlhtml

前记

关于实现一个iOS动画,若是简单的,咱们能够直接调用UIView的代码块来实现,虽然使用UIView封装的方法很方便,可是这只能用于一些简答的动画,若是是一些复杂的动画呢?这就不得不去研究下核心动画Core Animation(包含在Quartz Core框架中)了。这这以前咱们必须了解,CALayer就包含在Quartz Core框架中,这是一个跨平台的框架,既能够用在iOS中又能够用在Mac OS X中。在使用Core Animation开发动画的本质就是将CALayer中的内容转化为位图从而供硬件操做,因此要熟练掌握动画操做必须熟悉CALayer,关于CALayer就不在这里讲了。今天主要是分析核心动画,iOS 中的核心动画又分为下面几种:基础动画、关键帧动画、动画组、转场动画、弹簧动画。下面咱们先来了解下各个动画之间的关系spring

core_animation.jpeg

动画简介
CAAnimation
@interface CAAnimation : NSObject
    <NSCoding, NSCopying, CAMediaTiming, CAAction>
{
@private
  void *_attr;
  uint32_t _flags;
}

这是核心动画的基类,不能直接使用,主要负责动画的时间、速度等,从上面能够看出是准守CAMediaTiming协议的。并发

CAPropertyAnimation

属性动画的基础类,继承自CAAnimation,不能直接使用。何谓属性动画呢?即经过修改属性值就能够产生动画效果。框架

CAAnimationGroup

动画组,继承自CAAnimation,顾名思义就是一种组合动画,能够经过动画组来进行全部动画行为的统一控制,组中全部动画效果能够并发执行。函数

CATransition

转场动画,继承自CAAnimation,主要是经过滤镜来进行动画的效果设置动画

CABasicAnimation

基础动画,继承自CAPropertyAnimation,经过属性控制动画的参数,只要初始状态和结束状态ui

CAKeyframeAnimation

关键帧动画,继承自CAPropertyAnimation,也是经过属性控制动画参数,可是与基础动画不一样的是有多个控制状态,而且能够经过path来实现动画url

CASpringAnimation

弹簧动画,是在iOS 9中引入的,继承自CABasicAnimation,用于制做弹簧动画3d

动画使用

在使用动画以前,先补充个知识点---UIBezierPath, 这在动画使用的过程当中会常常用到code

核心动画

要使用核心动画,咱们必须先了解下其属性,这里咱们先看其遵照的协议<CAMediaTiming>

属性 说明
beginTime 指定动画开始的时间。开始延迟几秒的话,设置为CACurrentMediaTime() + 秒数 的方式便可
duration 动画的时长
speed 动画的速度
timeOffset 详细说明
repeatCount 动画重复的次数,若是要一直持续设置为HUGE_VALF便可
repeatDuration 设置动画的时间。在该时间内动画一直执行,不计次数
autoreverses 动画结束时是否执行逆动画
fillMode 分四种状况,分别为kCAFillModeForwardskCAFillModeBackwardskCAFillModeBothkCAFillModeRemoved,决定当前对象在非active时间段的行为,好比动画开始以前或者动画结束之

CAAnimation属性

属性 说明
timingFunction 速度控制函数,控制动画运行的节奏
removedOnCompletion 默认为YES,表明动画执行完毕后就从图层上移除,图形会恢复到动画执行前的状态。若是想让图层保持显示动画执行后的状态,那就设置为NO,不过还要设置fillModekCAFillModeForwards

关于fillMode的四种状况:

  • kCAFillModeRemoved 默认值,动画结束后,layer会恢复到以前的状态

  • kCAFillModeForwards 当动画结束后,layer会一直保持着动画最后的状态,而removedOnCompletion的默认属性值是 YES,因此为了使动画结束以后layer保持结束状态,应将removedOnCompletion设置为NO

  • kCAFillModeBackwards 在动画开始前,只须要将动画加入了一个layer,layer便当即进入动画的初始状态并等待动画开始。

  • kCAFillModeBoth 这个其实就是上面两个的合成,动画加入后开始以前,layer便处于动画初始状态,动画结束后layer保持动画最后的状态

关于速度CAMediaTimingFunction控制的四种状况

  • kCAMediaTimingFunctionLinear(线性):匀速,给你一个相对静态的感受

  • kCAMediaTimingFunctionEaseIn(渐进):动画缓慢进入,而后加速离开

  • kCAMediaTimingFunctionEaseOut(渐出):动画全速进入,而后减速的到达目的地

  • kCAMediaTimingFunctionEaseInEaseOut(渐进渐出):动画缓慢的进入,中间加速,而后减速的到达目的地。这个是默认的动画行为。

属性动画

从上图中咱们知道,属性动画是继承自核心动画,在其API中咱们能够看到有以下函数和属性

+ (instancetype)animationWithKeyPath:(nullable NSString *)path;

@property(nullable, copy) NSString *keyPath;

其中都有keyPath,这又是什么呢?这就是属性动画与动画组和转场动画不一样之处。经过指定CALayer的一个属性名称为keyPath(NSString类型),而且对CALayer的这个属性的值进行修改,达到相应的动画效果。好比,指定@"opacity"keyPath,就修改CALayeropacity属性的值,以达到透明度变化的动画效果
下面咱们列举一些经常使用的animationWithKeyPath

经常使用值 说明 使用方式
transform 3D变换 [NSValue valueWithCATransform3D:CATransform3DMakeScale(0.5, 0.5, 0)],直接进行3D变换
transform.scale 缩放 @(1.5) ,放大1.5倍
transform.scale.x 宽度缩放 @(1.5) ,宽放大1.5倍
transform.scale.y 高度缩放 @(1.5) ,高放大1.5倍
transform.rotation.x 围绕x轴旋转 @(M_PI) ,x轴旋转180度
transform.rotation.y 围绕y轴旋转 @(M_PI) ,y轴旋转180度
transform.rotation.z 围绕z轴旋转 @(M_PI) ,z轴旋转180度
position 位置(中心点的改变) [NSValue valueWithCGPoint:CGPointMake(100, 100)],中心点变为(100,100)
opacity 透明度 @(0.5) ,透明度变为0.5
bounds 大小的改变 中心点保持不变 [NSValue valueWithCGRect:CGRectMake(0, 0, 300, 300)],大小变为(300,300)
cornerRadius 圆角的设置 @(5) ,圆角设置为5
backgroundColor 背景颜色变换 (id)[UIColor redColor].CGColor,背景改成红色
contents 能够改变layer展现的图片 (id)[UIImage imageNamed:@"12.png"].CGImage,将UIView的展现图片改成12.png
strokeStart 从起始点开始变化 fromValue = 0toValue = 1,为CAShapeLayer的属性
strokeEnd 从结束的位置开始变化 fromValue = 1toValue = 0.5,为CAShapeLayer的属性
path 根据路径来改变 fromValue = (__bridge id)(start.CGPath);toValue = (__bridge id)((end.CGPath))
基础动画

基础动画是继承自属性动画,因此在使用的时候,咱们最主要的就是经过属性来控制动画,好比设置初始值、结束值,固然还有核心动画的其余属性。

- (void)showAnimation
{
    CAShapeLayer * circle = [CAShapeLayer layer];
    circle.frame = self.view.bounds;
    
    //
    UIBezierPath  * circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(CGRectGetMidX(self.view.bounds), CGRectGetMidY(self.view.bounds)) radius:self.normalSize.width/2 -1 startAngle:0 endAngle:2*M_PI clockwise:YES];
    
    circle.path = circlePath.CGPath;
    circle.strokeColor = [UIColor blueColor].CGColor;
    circle.fillColor = nil;
    [self.view.layer addSublayer:circle];
    
    //经过圆的strokeStart 改变来进行改变
    CABasicAnimation * strokeStartAnimation = [CABasicAnimation animationWithKeyPath:@"strokeStart"];
    strokeStartAnimation.fromValue = @(0.2);
    strokeStartAnimation.toValue = @(0);
    
    //经过圆的strokeEnd 改变来进行改变
    CABasicAnimation * strokeEndAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    strokeEndAnimation.fromValue = @0.5;
    strokeEndAnimation.toValue = @(1.0);
    
    //经过圆的transform.rotation.z 改变来进行改变
    CABasicAnimation * rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    rotationAnimation.fromValue = @(0);
    rotationAnimation.toValue = @(-M_PI * 2);
    
    //组合动画
    CAAnimationGroup * group = [CAAnimationGroup animation];
    group.duration = 5;
    group.removedOnCompletion = NO;
    group.fillMode = kCAFillModeForwards;
    group.animations = @[strokeStartAnimation,strokeEndAnimation,rotationAnimation];
    [circle addAnimation:group forKey:nil];
    
}

在上面的基础动画代码中,我只用了最基础的两个属性,fromValuetoValue,而其它属性呢?因为后面用到了动画组,因此讲其它属性在动画组进行了设置。[circle addAnimation:group forKey:nil];这句代码中,key我设置的为nil,若是不设置为nil的时候,是什么意思呢?这个其实就是咱们的动画设置了一个key,能够用来区别是哪个动画,在后面我会举例说明。
上面代码对应的效果以下:

baseAnimation.gif

关键帧动画

关键帧动画也是属性动画,与基础动画最主要不一样的是在两个参数上,NSArray *valuesCGPathRef path,经过这个咱们能够知晓,关键帧动画能够设置多个控制状态
以下:

//用value的方式进行展现动画
- (void)showKeyFrameAnimationWithValues
{
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    NSValue *key1 = [NSValue valueWithCGPoint:_beginPoint];
    NSValue *key2 = [NSValue valueWithCGPoint:CGPointMake(100, 100)];
    NSValue *key3 = [NSValue valueWithCGPoint:CGPointMake(150, 50)];
    NSValue *key4 = [NSValue valueWithCGPoint:CGPointMake(300, 250)];
    animation.values = @[key1,key2,key3,key4];
    animation.duration = 5.0;
    animation.delegate = (id)self;
//    animation.autoreverses = true;//是否按路径返回
//    animation.repeatCount = HUGE;//是否重复执行
    animation.removedOnCompletion = NO;//执行后移除动画
    animation.fillMode = kCAFillModeForwards;
    
    //存储位置
    [animation setValue:key4 forKey:@"keyframeAnimationLocation"];
    
    [_fishImageView.layer addAnimation:animation forKey:@"keyframeAnimation_fish"];
}

并且还能够设置路径,这也是其最大的特色
以下:

//用CGPathRef的方式进行展现动画
- (void)showKeyFrameAnimationWithPath
{
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];

    UIBezierPath *path = [UIBezierPath bezierPath];
    [path moveToPoint:_fishImageView.layer.position];
    //三次贝塞尔曲线
    [path addCurveToPoint:CGPointMake(300, 200) controlPoint1:CGPointMake(150, 400) controlPoint2:CGPointMake(230, -100)];
    
    animation.path = path.CGPath;
    animation.duration = 5.0;
    animation.delegate = (id)self;
    [_fishImageView.layer addAnimation:animation forKey:@"keyframeAnimation_path_fish"];
}

效果以下,这里就暂时只给出values的效果

keyanimation.gif

在上面的两个方法中,对layer设置了两个不一样的key,分别为keyframeAnimation_fishkeyframeAnimation_path_fish
前面我提到过,经过这个能够判断是哪种动画,这里咱们在动画结束的地方进行区分一下

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag
{
    if ([anim isEqual:[_fishImageView.layer animationForKey:@"keyframeAnimation_fish"]])
    {
//        [CATransaction begin];
//        //禁用隐式动画
//        [CATransaction setDisableActions:YES];
        
        _fishImageView.layer.position = [[anim valueForKey:@"keyframeAnimationLocation"] CGPointValue];
   
//        //提交事务
//        [CATransaction commit];
    }
    else if ([anim isEqual:[_fishImageView.layer animationForKey:@"keyframeAnimation_path_fish"]])
    {
    
    }
}
动画组

动画组其实很简单,就是将许多动画组合在一块儿
在上面的基础动画中,我也用到了动画组,下面再贴上一组动画组合效果

//发射
- (void)launchAnimation
{
    CABasicAnimation *animation1 = [CABasicAnimation animationWithKeyPath:@"transform.scale.y"];
    animation1.fromValue = @(1.0);
    animation1.toValue = @(1.5);
    
    CABasicAnimation *animation2 = [CABasicAnimation animationWithKeyPath:@"transform.scale.x"];
    animation2.fromValue = @(1.0);
    animation2.toValue = @(1.5);
    
    CABasicAnimation *animation3 = [CABasicAnimation animationWithKeyPath:@"position"];
    animation3.fromValue = [NSValue valueWithCGPoint:_ballLayer.position];
    animation3.toValue = [NSValue valueWithCGPoint:CGPointMake(self.view.frame.size.width - (30 * 1.3)/2.0 , _ballLayer.position.y - 200)];
    
    
    CAAnimationGroup *anima = [CAAnimationGroup animation];
    anima.animations = @[animation1, animation2,animation3];
    anima.duration = 1.0;
    anima.delegate = (id)self;
    anima.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    anima.fillMode = kCAFillModeForwards;
    anima.removedOnCompletion = NO;
    
    [_ballLayer addAnimation:anima forKey:@"group_launch"];
}

在后面的demo里有完整的代码,这里只是截取了部分代码
效果以下:

groupanimation.gif

转场动画

在了解转场动画以前,咱们先了解其两个参数,经过这些参数,咱们就能很清楚的了解其效果

  • type转场类型
转场动画类型 说明 常量 是否支持方向设置
公开API
fade 淡出 kCATransitionFade
movein 新视图移动到旧视图上面 kCATransitionMoveIn
push 新视图退出旧视图 kCATransitionPush
reveal 移开旧视图显示新的 kCATransitionReveal
私有API 苹果未公开的类型,可是目前仍是能够用的 私有API只能经过下面的字符串进行访问
cube 立体翻转
oglFlip 翻转
suckEffect 收缩
rippleEffect 水滴波纹效果
pageCurl 向上翻页效果
pageUnCurl 向下翻页效果
cameraIrisHollowOpen 摄像头打开效果
cameraIrisHollowClose 摄像头关闭效果
  • subtype动画子类型
属性 说明
kCATransitionFromRight 从右
kCATransitionFromLeft 从左
kCATransitionFromTop 从顶部
kCATransitionFromBottom 从底部

在了解上面两个属性后,对应转场动画,就差很少了
部分代码以下

- (void)transition:(BOOL)next
{
    CATransition *transition = [CATransition animation];
    transition.type = @"cube";
    if (next)
    {
        transition.subtype = kCATransitionFromLeft;
    }
    else
    {
        transition.subtype = kCATransitionFromRight;
    }
    transition.duration = 1.0;
    
    _imageView.image = [UIImage imageNamed:[NSString stringWithFormat:@"%ld.jpg",(long)_currentIndex]];
    [_imageView.layer addAnimation:transition forKey:@"transitionAnimation"];
}

效果以下

transanimation.gif

弹簧动画

弹簧动画是在iOS 9后才出现的,在这以前,咱们能够经过下面的方法来实现

[UIView animateWithDuration:5.0 delay:0 usingSpringWithDamping:0.1 initialSpringVelocity:1.0 options:UIViewAnimationOptionCurveLinear animations:^{

    } completion:nil];

iOS 9后苹果公开了这一API,咱们先对其中的属性进行分析,由于代码中有注释,因此就直接贴一部分代码

//质量,影响图层运动时的弹簧惯性,质量越大,弹簧拉伸和压缩的幅度越大
        positionAnimation.mass = 0.1;
        //阻尼系数,阻止弹簧伸缩的系数,阻尼系数越大,中止越快
        positionAnimation.damping = 2;
        //刚度系数(劲度系数/弹性系数),刚度系数越大,形变产生的力就越大,运动越快
        positionAnimation.stiffness = 50;
        //初始速率,动画视图的初始速度大小
        //速率为正数时,速度方向与运动方向一致,速率为负数时,速度方向与运动方向相反
        positionAnimation.initialVelocity = -10;

关于弹簧动画,我也写了一个例子,效果以下

springanimation.gif

项目文件截图:

写在最后

关于核心动画,差很少就简单的介绍这么点,若有什么不对的还望各位多多指教,不甚感激。iOS 核心动画 Core Animation浅谈

代码地址以下:
http://www.demodashi.com/demo/11603.html

注:本文著做权归做者,由demo大师代发,拒绝转载,转载须要做者受权

相关文章
相关标签/搜索