属性动画做用于图层的某一个单一的属性, 并指定它的目标值, 或者一连串要作动画的值. 属性动画分为基础动画
和关键帧动画
.数组
动画就是一段时间内发生的改变, 最简单的形式就是从一个值改变到另外一个值, 这也是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];
第七章的隐式动画中, 能够用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, 计时你使用动画类型所声明的属性并不匹配, 你也能够随意的使用键值对.
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]; }
**效果: **
属性动画针对的是一个键, 也就意味着能够对子属性甚至虚拟属性作动画. 例如, 若是须要用到旋转则须要使用 transform
属性, 可是使用transform直接旋转2*M_PI, 实际上不会作任何变化, 使用起来仍是很是不方便的, 这时候可使用transform的虚拟属性. transform.rotation
, 建立CABaseAnimation动画, 设置keypath是 trabsfirm.rotation
, 设置byValue, 就能够直接对图层进行旋转操做, 比使用 CATransform3D方便的多.
好处以下:
transform.rotation
属性有个奇特的问题是, 它并不存在. 由于CATransform3D并非一个对象, 它其实是一个结构体, 也没有符合KVC相关属性, transform.rotation其实是一个CALayer用于处理动画变换的虚拟属性.
你不能够直接设置transform.rotation
或者transform.scale
, 他们不能被直接使用. 当你对他们作动画时候, CoreAnimation自动的根据经过CAValueFunction来计算的值来更新transform属性.
CAValueFunction用于咱们赋值给虚拟的transform.rotation
简单浮点值转换成真正的用于摆放图层的CATransfor3D矩阵值. 你能够经过CAPropertyAnimation的valueFunction属性来改变, 因而你设置的函数将会覆盖默认的函数.
CAValueFunction看起来彷佛是对那些不能简单相加的属性(例如变换矩阵)作动画的很是有用的机制,但因为CAValueFunction的实现细节是私有的,因此目前不能经过继承它来自定义。你能够经过使用苹果目前已近提供的常量(目前都是和变换矩阵的虚拟属性相关,因此没太多使用场景了,由于这些属性都有了默认的实现方式)。
经过虚拟属性这一小节, 应该弄明白, CAPropertyAnimation的valueFunction的做用是什么.
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];
对iOS应用程序来讲, 属性动画只对图层的可动动画属性起做用, 可是要改版夜歌不能动画属性(好比图片), 或者从层级关系中添加或者移除图层, 属性动画将不起做用.
因而就有了过分动画的概念. 过分动画并不像属性动画那亚航平滑的在两个值之间作作动画, 而是影响到整个图层的变化. 过分动画首先须要展现以前的图层外观, 而后经过一个交换过分到新的外观. 建立过分动画咱们使用的是 CATransition
, 一样是另外一个CAAnimation子类, 和别的子类不一样, CATransform有一个type和subtype来标识变换效果. type是NSString类型值:
kCATransitionFade //淡入淡出 kCATransitionMoveIn //见名知意... kCATransitionPush kCATransitionReveal
subtype用来控制动画移动的方向:
kCATransitionFade kCATransitionMoveIn kCATransitionPush kCATransitionReveal
当设置CALayer的contents属性的时候, CATransition的确是默认的行为. 可是对于与视图关联的图层, 或者是其余隐式动画的欣慰, 这个特性依然是被禁用的, 可是对于本身建立的图层, 这意味着对图层contents图片作的改动都会自动附上淡入淡出的动画.
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]; }
过渡动画是一种对那些不太好作平滑动画的属性的强大工具, 可是提供的动画类型太少了.
另外苹果经过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,或者用私有的截屏方式就没有这个限制了。
以前提到过,你能够用-addAnimation:forKey:方法中的key参数来在添加动画以后检索一个动画,使用以下方法:
- (CAAnimation *)animationForKey:(NSString *)key;
但并不支持在动画运行过程当中修改动画,因此这个方法主要用来检测动画的属性,或者判断它是否被添加到当前图层中。
为了终止一个指定的动画,你能够用以下方法把它从图层移除掉:
- (void)removeAnimationForKey:(NSString *)key;
或者移除全部动画:
- (void)removeAllAnimations;
动画一旦被移除,图层的外观就马上更新到当前的模型图层的值。通常说来,动画在结束以后被自动移除,除非设置removedOnCompletion为NO,若是你设置动画在结束以后不被自动移除,那么当它不须要的时候你要手动移除它;不然它会一直存在于内存中,直到图层被销毁。