iOS核心动画笔记8-显式动画

显式动画

1. 属性动画

属性动画做用于图层的某一个单一的属性, 并指定它的目标值, 或者一连串要作动画的值. 属性动画分为基础动画关键帧动画.数组

1.1 基础动画

动画就是一段时间内发生的改变, 最简单的形式就是从一个值改变到另外一个值, 这也是CABaseAnimation的最主要功能. CABaseAnimation是CAPropertyAnimation的一个子类, 而CAPropertyAnimation的父类是CAAnimation, CAAnimation同时也是CoreAnimation全部动画类型的抽象基类. 做为抽象类, 事实上CAAnimation并无完成太多工做.函数

CAPropertyAnimation经过制定动画的keyPath做用于一个单一属性, CAAnimation一般做用于一个指定的CALayer, 因而这里的keyPath指的是一个图层的路径了. 实际上它是关键路径而不只仅是一个属性的名称, 就是说, 动画不只仅能够做用于图层自己的属性, 并且还包含了它的子成员的属性, 甚至包含虚拟属性.工具

CABaseAnimation继承自CAPropertyAnimation, 并添加了以下三个属性:性能

// 动画开始以前属性的值
@property(nullable, strong) id fromValue;
// 动画结束以后属性的值
@property(nullable, strong) id toValue;
// 动画执行过程当中, 改变的值
@property(nullable, strong) id byValue;

fromValue, toValue, byValue能够用多种不一样的方式进行组合, 可是为了防止冲突, 不能一次性指定三个值. 须要注意的是, 若是是图片必须转换成CGImageRef类型桥接成id类型; 若是是颜色也必须转换成CGColorRef类型桥接成id类型 (__bridge id) .动画

一个简单的改变背景颜色的基础动画(实际上若是是单独的layer, 用隐式动画比较好):url

// 基础动画, 改变颜色
- (void)changeColor {
    
    UIView *view = [[UIView alloc] initWithFrame:CGRectMake(10, 70, 80, 80)];
    [self.view addSubview:view];
    view.backgroundColor = [UIColor redColor];

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
    animation.toValue = (__bridge id)[UIColor purpleColor].CGColor;
    animation.duration = 10;
    [view.layer addAnimation:animation forKey:nil];
    
}

如上代码, 存在问题是, 当动画结束以后图层颜色立刻变回原来的颜色了, 缘由是作动画时候的图层是呈现层, 动画结束以后呈现层被移除, 因此颜色又变回原来的颜色了. 一种解决方案以下:代理

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(10, 70, 80, 80)];
    [self.view addSubview:view];
    view.backgroundColor = [UIColor redColor];

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"backgroundColor"];
    animation.fromValue = (__bridge id) view.layer.backgroundColor;
    animation.toValue = (__bridge id)[UIColor purpleColor].CGColor;
    animation.duration = 3;
    [view.layer addAnimation:animation forKey:nil];
    
    view.backgroundColor = [UIColor purpleColor];

1.2 CAAnimationDelegate

第七章的隐式动画中, 能够用CATransaction完成块中检测动画的完成, 可是在显式动画中显然是不可能的, 由于这里的动画和事务并无鸟关联.code

在CAAnimationDelegate的代理方法中, 咱们能够获取动画的开始和结束.orm

以下:视频

/* Called when the animation begins its active duration. */

- (void)animationDidStart:(CAAnimation *)anim {
    
    NSLog(@"animation start");
}

/* Called when the animation either completes its active duration or
 * is removed from the object it is attached to (i.e. the layer). 'flag'
 * is true if the animation reached the end of its active duration
 * without being removed. */

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    
    NSLog(@"animation finish");
}

咱们不能经过隐式动画来实现UIView的实例, 由于全部图层的隐式动画都被禁用了. 咱们能够经过简单的UIView动画实现简单的动画效果, 可是若是想要更好的控制动画时间, 使用显式动画比较好.

动画的代理方法中, 动画自己会做为一个参数传入委托方法中, 可是须要注意: 委托传入的动画参数是原始值的一个深拷贝, 而不是同一个值.

事实上, 在项目中咱们用来惟一标识一个动画的方法是使用key值, 通常咱们经过 -setValue:forKey:-valueForKey: 这两个方法来存取. 可是CAAnimation有一个不提欧宁的性能, 它更像一个NSDictionary, 计时你使用动画类型所声明的属性并不匹配, 你也能够随意的使用键值对.

1.2 关键帧动画

CAKeyframeAnimation是另外一种UIKit没有暴露出来但功能强大的类. 它也是CAPropertyAnimation的一个子类, 也做用于单一的一个属性, 可是它能够根据已连串随意的值来作动画.

关键帧起源于传动动画, 意思是指主导动画在显著改变发生时重绘当前帧(也就是关键帧), 没帧之间剩下的绘制经过关键帧推算出来. CAKeyFrameAnimation也是一样的道理, 你提供了显著帧以后, CoreAnimation在每帧之间进行插入.

关键帧动画, 能够提供一个数组, 数组中包含一组相关属性的值, 而后CoreAnimation会将这些值以动画的形式展现出来, 两个值之间的值CoreAnimation会计算出来, 可是, 关键帧动画开始执行时候会直接跳转到第一帧的值, 而并不会将当前的值平滑过渡到第一帧, 这时候, 咱们通常须要将第一帧设置为当前颜色值相同的值. 一样的最后一帧也要和最终的颜色想等, 否则会有跳跃.

// 2. 关键帧动画, 使用颜色数组改变颜色
- (void)keyFrameAnimationChangeColor {
    
    CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"backgroundColor"];
    keyAnimation.duration = 7;
    
    keyAnimation.values = @[(__bridge id)self.colorView.backgroundColor.CGColor,
                            (__bridge id)[UIColor blueColor].CGColor,
                            (__bridge id)[UIColor redColor].CGColor,
                            (__bridge id)[UIColor blackColor].CGColor,
                            (__bridge id)[UIColor purpleColor].CGColor,
                            (__bridge id)[UIColor cyanColor].CGColor,
                            (__bridge id)[UIColor yellowColor].CGColor];
    [self.colorView.layer addAnimation:keyAnimation forKey:nil];
    // 配置完动画后, 更改view颜色, 避免动画结束以后颜色忽然跳跃式变化回原来颜色.
    self.colorView.layer.backgroundColor = [UIColor yellowColor].CGColor;
}

通常来讲, 经过数组描述动画的运动并不直观. CAKeyFrameAnimation能够用另外一种方式指定动画 CGPath . path属性能够用一种直观的方式, 使用CoreGraphics函数定义运动序列来绘制动画路径.

// 3. 关键帧动画,
- (void)pathAnimation {
    
    // 使用贝塞尔画曲线
    UIBezierPath *bPath = [UIBezierPath bezierPath];
    [bPath moveToPoint:CGPointMake(30, 250)];
    [bPath addCurveToPoint:CGPointMake(350, 250) controlPoint1:CGPointMake(100, 150) controlPoint2:CGPointMake(250, 400)];
    
    
    // 将曲线添加到shapeLayer上面
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.path = bPath.CGPath;
    shapeLayer.lineWidth = 0.5;
    shapeLayer.strokeColor = [UIColor redColor].CGColor;
    shapeLayer.fillColor = [UIColor clearColor].CGColor;
    [self.view.layer addSublayer:shapeLayer];
    
    
    
    //建立飞机控件
    UIImageView *plane = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
    plane.center = CGPointMake(30, 150);
    plane.backgroundColor = [UIColor blueColor];
    [self.view addSubview:plane];
    
    
    // 建立path动画
    CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    keyAnimation.path = bPath.CGPath;
    keyAnimation.duration = 3.f;
// 设置图形方向跟随曲线切面方向自动作旋转动做.
    keyAnimation.rotationMode = kCAAnimationRotateAuto;
    [plane.layer addAnimation:keyAnimation forKey:nil];
}

**效果: **

1.3 虚拟属性

属性动画针对的是一个键, 也就意味着能够对子属性甚至虚拟属性作动画. 例如, 若是须要用到旋转则须要使用 transform 属性, 可是使用transform直接旋转2*M_PI, 实际上不会作任何变化, 使用起来仍是很是不方便的, 这时候可使用transform的虚拟属性. transform.rotation, 建立CABaseAnimation动画, 设置keypath是 trabsfirm.rotation, 设置byValue, 就能够直接对图层进行旋转操做, 比使用 CATransform3D方便的多.

好处以下:

  • 能够不经过关键帧动画旋转大于180°.
  • 能够用相对值进行旋转而不是绝对值.(设置byValue而不是toValue).
  • 能够不建立CATransform3D, 而使用一个简单的数值来指定角度.
  • 不会和transform.position或者transform.scale冲突(一样是使用关键路径来作独立的动画属性).

transform.rotation 属性有个奇特的问题是, 它并不存在. 由于CATransform3D并非一个对象, 它其实是一个结构体, 也没有符合KVC相关属性, transform.rotation其实是一个CALayer用于处理动画变换的虚拟属性.

你不能够直接设置transform.rotation或者transform.scale, 他们不能被直接使用. 当你对他们作动画时候, CoreAnimation自动的根据经过CAValueFunction来计算的值来更新transform属性.

CAValueFunction用于咱们赋值给虚拟的transform.rotation简单浮点值转换成真正的用于摆放图层的CATransfor3D矩阵值. 你能够经过CAPropertyAnimation的valueFunction属性来改变, 因而你设置的函数将会覆盖默认的函数.

CAValueFunction看起来彷佛是对那些不能简单相加的属性(例如变换矩阵)作动画的很是有用的机制,但因为CAValueFunction的实现细节是私有的,因此目前不能经过继承它来自定义。你能够经过使用苹果目前已近提供的常量(目前都是和变换矩阵的虚拟属性相关,因此没太多使用场景了,由于这些属性都有了默认的实现方式)。

经过虚拟属性这一小节, 应该弄明白, CAPropertyAnimation的valueFunction的做用是什么.

1.4 动画组

CABaseAnimation的CAKeyframeAnimation仅仅做用于单独的属性, 而CAAnimationGroup能够把这些动画组合在一块儿. CAAnimationGroup是另外一个继承自CAAnimation的子类, 它添加了一个animations数组的属性, 用来组合别的动画.

动画组用起来也是巨简单. 建立对象, 而后将其它动画放到animations数组中便可.

// 建立path动画
    CAKeyframeAnimation *keyAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    keyAnimation.path = bPath.CGPath;
    keyAnimation.duration = 3.f;
    keyAnimation.rotationMode = kCAAnimationRotateAuto;
 
    // 改变颜色的动画
    CAKeyframeAnimation *colorAnimation = [CAKeyframeAnimation animationWithKeyPath:@"backgroundColor"];
    colorAnimation.duration = 7;
    colorAnimation.values = @[(__bridge id)self.colorView.backgroundColor.CGColor,
                            (__bridge id)[UIColor blueColor].CGColor,
                            (__bridge id)[UIColor redColor].CGColor,
                            (__bridge id)[UIColor blackColor].CGColor,
                            (__bridge id)[UIColor purpleColor].CGColor,
                            (__bridge id)[UIColor cyanColor].CGColor,
                            (__bridge id)[UIColor yellowColor].CGColor];
    
    // animationGroup 代码.
    CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
    animationGroup.duration = 7;
    animationGroup.animations = @[keyAnimation, colorAnimation];
    
    [plane.layer addAnimation:animationGroup forKey:nil];

2. 过渡动画

对iOS应用程序来讲, 属性动画只对图层的可动动画属性起做用, 可是要改版夜歌不能动画属性(好比图片), 或者从层级关系中添加或者移除图层, 属性动画将不起做用.

因而就有了过分动画的概念. 过分动画并不像属性动画那亚航平滑的在两个值之间作作动画, 而是影响到整个图层的变化. 过分动画首先须要展现以前的图层外观, 而后经过一个交换过分到新的外观. 建立过分动画咱们使用的是 CATransition , 一样是另外一个CAAnimation子类, 和别的子类不一样, CATransform有一个type和subtype来标识变换效果. type是NSString类型值:

kCATransitionFade   //淡入淡出
kCATransitionMoveIn  //见名知意...
kCATransitionPush 
kCATransitionReveal

subtype用来控制动画移动的方向:

kCATransitionFade 
kCATransitionMoveIn 
kCATransitionPush 
kCATransitionReveal

2.1 隐式过分

当设置CALayer的contents属性的时候, CATransition的确是默认的行为. 可是对于与视图关联的图层, 或者是其余隐式动画的欣慰, 这个特性依然是被禁用的, 可是对于本身建立的图层, 这意味着对图层contents图片作的改动都会自动附上淡入淡出的动画.

2.2 对图层树的动画

CATransition并不做用于指定的图层属性, 就是说, 能够在不能准确得知改变什么的状况下对图层作动画, 六在不知道UITableView哪一行被添加或者移除的状况下就能够平滑的刷新它, 或者在不知道UIViewCVontroller内部视图层级的状况下对两个不一样的实例作过分动画. 这些例子的变换不涉及到图层的属性, 而是整个图层树的改变, 咱们在这种动画的过程当中手动的在层级关系中添加或者移除图层.

注意: 要确保CATransition添加到的图层在过分动画发生时候不会再树状结构中被移除, 不然CATransition会和图层一块儿被移除, 通常来讲, 只须要将动画添加到被影响图层的superlayer.

例如, 对UITabBarController的视图图层作, 切换标签时候淡入淡出效果.

// 首先设置代理, 而后实现代理方法, 在代理中添加过分动画.
// 自定义切换tabbar时候的过分动画
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController {
    
    CATransition *transition = [CATransition animation];
    transition.type = kCATransitionFade;
    transition.duration = 0.2f;
//    transition.subtype = kCATransitionFromLeft;
    [self.view.layer addAnimation:transition forKey:nil];
}

2.3 自定义动画

过渡动画是一种对那些不太好作平滑动画的属性的强大工具, 可是提供的动画类型太少了.
另外苹果经过UIView +transitionFromView:toView:duration:options:completion:和+transitionWithView:duration:options:animations:方法提供了Core Animation的过渡特性。可是这里的可用的过渡选项和CATransition的type属性提供的常量彻底不一样。UIView过渡方法中options参数能够由以下常量指定:

UIViewAnimationOptionTransitionFlipFromLeft 
UIViewAnimationOptionTransitionFlipFromRight
UIViewAnimationOptionTransitionCurlUp 
UIViewAnimationOptionTransitionCurlDown
UIViewAnimationOptionTransitionCrossDissolve 
UIViewAnimationOptionTransitionFlipFromTop 
UIViewAnimationOptionTransitionFlipFromBottom

可是在iOS平台上仍是能够作自定义的过渡动画的. 原理是: 先对目标view进行截图, 将截图放在原来view的上面, 而后对截图进行一些动画操做, 实现自定义过渡动画.

截图: CALayer中有个-renderInContext:方法,能够经过把它绘制到Core Graphics的上下文中捕获当前内容的图片,而后在另外的视图中显示出来。若是咱们把这个截屏视图置于原始视图之上,就能够遮住真实视图的全部变化,因而从新建立了一个简单的过渡效果。

这里有个警告:-renderInContext:捕获了图层的图片和子图层,可是不能对子图层正确地处理变换效果,并且对视频和OpenGL内容也不起做用。可是用CATransition,或者用私有的截屏方式就没有这个限制了。

3. 在动画过程当中取消动画

以前提到过,你能够用-addAnimation:forKey:方法中的key参数来在添加动画以后检索一个动画,使用以下方法:

- (CAAnimation *)animationForKey:(NSString *)key;

但并不支持在动画运行过程当中修改动画,因此这个方法主要用来检测动画的属性,或者判断它是否被添加到当前图层中。

为了终止一个指定的动画,你能够用以下方法把它从图层移除掉:

- (void)removeAnimationForKey:(NSString *)key;

或者移除全部动画:

- (void)removeAllAnimations;

动画一旦被移除,图层的外观就马上更新到当前的模型图层的值。通常说来,动画在结束以后被自动移除,除非设置removedOnCompletion为NO,若是你设置动画在结束以后不被自动移除,那么当它不须要的时候你要手动移除它;不然它会一直存在于内存中,直到图层被销毁。

相关文章
相关标签/搜索