Core Animation
- 接下来详细介绍下动画的各个属性及作用
- fromValue: 动画的开始值(Any类型, 根据动画不同可以是CGPoint、NSNumber等)
- toValue: 动画的结束值, 和fromValue类似
- beginTime: 动画的开始时间
- duration : 动画的持续时间
- repeatCount : 动画的重复次数
- fillMode: 动画的运行场景
- isRemovedOnCompletion: 完成后是否删除动画
- autoreverses: 执行的动画按照原动画返回执行
- path:关键帧动画中的执行路径
- values: 关键帧动画中的关键点数组
- animations: 组动画中的动画数组
- delegate : 动画代理, 封装了动画的执行和结束方法
- timingFunction: 控制动画的显示节奏, 系统提供五种值选择,分别是:
1.kCAMediaTimingFunctionDefault( 默认,中间快)
2.kCAMediaTimingFunctionLinear(线性动画)
3.kCAMediaTimingFunctionEaseIn(先慢后快 慢进快出)
4.kCAMediaTimingFunctionEaseOut(先块后慢快进慢出)
5.kCAMediaTimingFunctionEaseInEaseOut(先慢后快再慢)
- type: 过渡动画的动画类型,系统提供了多种过渡动画, 分别是:
1: fade(淡出 默认)
2: moveIn(覆盖原图)
3: push(推出)
4: fade(淡出 默认)
5: reveal(底部显示出来)
6: cube(立方旋转)
7: suck(吸走)
8: oglFlip(水平翻转 沿y轴)
9: ripple(滴水效果)
10: curl(卷曲翻页 向上翻页)
11: unCurl(卷曲翻页返回 向下翻页)
12: caOpen(相机开启)
13: caClose(相机关闭)
- subtype : 过渡动画的动画方向, 系统提供了四种,分别是:
1.fromLeft( 从左侧)
2.fromRight(从右侧)
3.fromTop(有上面)
4.fromBottom(从下面
1. 基础动画( CABasicAnimation )
基础动画主要提供了对于CALayer对象中的可变属性进行简单动画的操作。比如:位移、旋转、缩放、透明度、背景色等。
基础动画根据 keyPath来区分不同的动画,,
系统提供了多个类型,如: transform.scale(比例转换)、transform.scale.x、transform.scale.y、 transform.rotation(旋转) 、transform.rotation.x(绕x轴旋转)、transform.rotation.y(绕y轴旋转)、transform.rotation.z(绕z轴旋转)、opacity(透明度)、margin、backgroundColor(背景色)、cornerRadius(圆角)、borderWidth(边框宽)、bounds、contents、contentsRect、cornerRadius、frame、hidden、mask、masksToBounds、shadowColor(阴影色)、shadowOffset、shadowOpacity、shadowOpacity, 在使用时候, 需要根据具体的需求选择合适的.
?一个简单的动画效果
ABasicAnimation *moveAnimation = [CABasicAnimation animationWithKeyPath:@"position.y"]; moveAnimation.duration = 0.8;//动画时间 //动画起始值和终止值的设置 moveAnimation.fromValue = @(self.imageView.center.x); moveAnimation.toValue = @(self.imageView.center.x-30); //一个时间函数,表示它以怎么样的时间运行 moveAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]; moveAnimation.repeatCount = HUGE_VALF; moveAnimation.repeatDuration = 2; moveAnimation.removedOnCompletion = NO; moveAnimation.fillMode = kCAFillModeForwards; //添加动画,后面有可以拿到这个动画的标识 [self.imageView.layer addAnimation:moveAnimation forKey:@"可以拿到这个动画的Key值"];
在addAnimation:forKey:方法中,也可以给这个动画设置一个键,可以在其他地方将其取出来,进行一些操作,比如删除。这也充分体现了kvc的灵活。
用到CALayer的 removeAnimationForKey:方法。
相关属性
keyPath:要改变的属性名称(传字符串)
fromValue:keyPath相应属性的初始值
toValue:keyPath相应属性的结束值
timingFunction:动画随时间运行的关系
、
2. 关键帧动画( CAKeyframeAnimation )
CAKeyframeAnimation和 CABasicAnimation都属于CAPropertyAnimatin的子类。不同的是 CABasicAnimation只能从一个数值(fromValue)变换成另一个数值(toValue),而 CAKeyframeAnimation则会使用一个数组(values) 保存一组关键帧, 也可以给定一个路径(path)制作动画。
- values:存放关键帧(keyframe)的数组,动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧 .
- path:可以设置一个 CGPathRef或 CGMutablePathRef,让层跟着路径移动. path只对 CALayer的 anchorPoint和 position起作用, 如果设置了path,那么values将被忽略.
- keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0, keyTimes中的每一个时间值都对应 values中的每一帧.当 keyTimes没有设置的时候,各个关键帧的时间是根据 duration平分的。
?一个关键帧动画代码
CAKeyframeAnimation *animaiton = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation"]; NSArray *rotationVelues = @[@(M_PI_4), @(-M_PI_4), @(M_PI_4)]; animaiton.values = rotationVelues; animation.rotationMode = kCAAnimationRotateAuto; //方向 animaiton.duration = 3.0f; animation.keyTimes = @[@0.2 ,@0.8 ,@1]; animation.path = bezierPath.CGPath; animaiton.repeatCount = HUGE_VALF; // #define HUGE_VALF 1e50f [self.imageView.layer addAnimation:animaiton forKey:nil];
属性说明
values:上述的NSArray对象。里面的元素称为“关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
path:可以设置一个CGPathRef、CGMutablePathRef,让图层按照路径轨迹移动。path只对CALayer的anchorPoint和position起作用。如果设置了path,那么values将被忽略
keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧。如果没有设置keyTimes,各个关键帧的时间是平分的
bezierPath:贝赛尔曲线路径,为动画提供一个动画移动的路线。
UIBezierPath - 贝赛尔曲线
//创建路径 UIBezierPath *bezierPath = [[UIBezierPath alloc] init]; [bezierPath moveToPoint:CGPointMake(0, 450)]; [bezierPath addCurveToPoint:CGPointMake(370, 500) controlPoint1:CGPointMake(350, 200) controlPoint2:CGPointMake(300, 600)]; //一个曲线 //路径样式 CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.path = bezierPath.CGPath; shapeLayer.fillColor = [UIColor clearColor].CGColor; //填充色<默认黑色> shapeLayer.strokeColor = [UIColor blueColor].CGColor; //线色 shapeLayer.lineWidth = 2; [self.view.layer addSublayer:shapeLayer];
UIBezierPath创建一个路径,画出一条曲线。栗子中 起点(0,450)、终点(370,500) 和 (350,200)、(370,500)来个点来确定线的路径。
CAShapeLayer对上面的线进行属性上的设置。最后添加到一个layer上。
++CABasicAnimation可看做是只有2个关键帧的CAKeyframeAnimation++
3. 组动画( CAAnimationGroup )
CAAnimationGroup 是 CAAnimation 的子类,可以保存一组动画对象,可以保存基础动画、关键帧动画等,数组中所有动画对象可以同时并发运行, 也可以通过实践设置为串行连续动画.
属性说明
animations:用来保存一组动画对象的NSArray
CAAnimationGroup *animationGroup = [CAAnimationGroup animation]; animationGroup.animations = @[animation,basicAnimation]; animationGroup.duration = 4; animation.repeatCount = 9; [_imageLayer addAnimation:animationGroup forKey:@"changeColor"];
4. 过渡动画( CATransition)
CATransition是 CAAnimation的子类,用于做过渡动画或者 转场动画,能够为层提供移出屏幕和移入屏幕的动画效果。
过渡动画通过 type设置不同的动画效果, CATransition有多种过渡效果, 但其实 Apple官方的SDK只提供了四种:
- fade 淡出 默认
- moveIn 覆盖原图
- push 推出
- reveal 底部显示出来
但私有API提供了其他很多非常炫的过渡动画,如 cube(立方旋转)、suckEffect(吸走)、oglFlip(水平翻转 沿y轴)、 rippleEffect(滴水效果)、pageCurl(卷曲翻页 向上翻页)、pageUnCurl(卷曲翻页 向下翻页)、cameraIrisHollowOpen(相机开启)、cameraIrisHollowClose(相机关闭)等。
注: 因 Apple不提供维护,并且有可能造成你的app审核不通过, 所以不建议开发者们使用这些私有API.
CATransition *caTransition = [CATransition animation]; caTransition.duration = 0.5; caTransition.delegate = self; caTransition.timingFunction = [CAMediaTimingFunction functionWithName:@"easeInEaseOut"];//切换时间函数 caTransition.type = kCATransitionReveal;//动画切换风格 caTransition.subtype = kCATransitionFromLeft;//动画切换方向 // 子视图交换位置 //[self.parentView exchangeSubviewAtIndex:0 withSubviewAtIndex:1]; //动画在父视图 [self.parentView.layer addAnimation:caTransition forKey:@"Key"];
动画属性
type:动画过渡类型
subtype:动画过渡方向
startProgress:动画起点(在整体动画的百分比)
endProgress:动画终点(在整体动画的百分比)
/ 导航栏切换 UIViewController *viewCtr = [[UIViewController alloc] init]; viewCtr.view.backgroundColor = [UIColor redColor]; [self.navigationController pushViewController:viewCtr animated:NO];// 动画设置 NO 效果比较好 [self.navigationController.view.layer addAnimation:caTransition forKey:@"animation"];
CALayer
它有一些方法和属性来做动画和变换,和UIView最大的不同是CALayer不处理用户的交互事件。
UIView和CALayer的关系 - 平行关系
- 每个UIView都有一个CALayer实例的图层属性。
- 视图的职责就是创建并管理这个图层。
- UIView和CALyer有着平行的层级关系,职责分离。
解释:为什么动画结束后返回原状态?
首先我们需要搞明白一点的是,layer动画运行的过程是怎样的?其实在我们给一个视图添加layer动画时,真正移动并不是我们的视图本身,而是 presentation layer 的一个缓存。动画开始时 presentation layer开始移动,原始layer隐藏,动画结束时,presentation layer从屏幕上移除,原始layer显示。这就解释了为什么我们的视图在动画结束后又回到了原来的状态,因为它根本就没动过。
这个同样也可以解释为什么在动画移动过程中,我们为何不能对其进行任何操作。
所以在我们完成layer动画之后,最好将我们的layer属性设置为我们最终状态的属性,然后将presentation layer 移除掉。
moveAnimation.removedOnCompletion = NO;
UIView中目前最常用的动画方法应该就是这个方法了
+(void)animateWithDuration:(NSTimeInterval)duration animations:(void (^)(void))animations completion:(void (^ nullable)(BOOL finished))completion ;
稍微复杂点的方法还是使用CALayer调用CAAnimation的API更为方便。
CAShapeLayer
CAShapeLayer是一个通过矢量图形而不是bitmap(位图)来挥之的图层子类。
你指定诸如颜色和线宽等属性,用CAPath来定义想绘制的图形,最后CAShapeLayer就自动渲染出来了。
优点
1.渲染快速。CAShapeLayer使用了硬件加速,绘制同一个图形会比用Core Graphics快很多。
2.高效使用内存。一个CAShapeLayer不需要像CALayer一样创建一个寄宿图,所以无论有多大,都不会占用大多的内存。
3.不会被图层边界裁剪掉。
4.不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图普通图层一样变得像素化。
创建一个CGPath
CAShapeLayer可以用来绘制所有能够通过CGPath来表示的形状。这个形状不一定要闭合,图层路径也不一定要不可破的,事实上你可以在一个图层上绘制好几个不同的形状。
你可以控制一些属性比如
lineWith(线宽,用点表示单位)、lineCap(线条结尾的样子)、和lineJoin(线条之间的结合点的样子)。
CAShapeLayer属性时CGPathRef类型,当时我们用 UIBezierPath 帮助类创建了图层路径,这样我们就不用考虑释放CGPath了。
贝赛尔曲线+CAShapeLayer
//创建圆角
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.imageView.frame.size.width, self.imageView.frame.size.height) cornerRadius:10];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.path = maskPath.CGPath;
self.imageView.layer.mask=maskLayer;
/画图
- (void)viewDidLoad { [super viewDidLoad]; //create path UIBezierPath *path = [[UIBezierPath alloc] init]; [path moveToPoint:CGPointMake(175, 100)]; [path addArcWithCenter:CGPointMake(150, 100) radius:25 startAngle:0 endAngle: 2*M_PI clockwise:YES]; [path moveToPoint:CGPointMake(150, 125)]; [path addLineToPoint:CGPointMake(150, 175)]; [path addLineToPoint:CGPointMake(125, 225)]; [path moveToPoint:CGPointMake(150, 175)]; [path addLineToPoint:CGPointMake(175, 225)]; [path moveToPoint:CGPointMake(100, 150)]; [path addLineToPoint:CGPointMake(200, 150)]; //create shape layer CAShapeLayer *shapeLayer = [CAShapeLayer layer]; shapeLayer.strokeColor = [UIColor redColor].CGColor; shapeLayer.fillColor = [UIColor clearColor].CGColor; shapeLayer.lineWidth = 5; shapeLayer.lineJoin = kCALineJoinRound; shapeLayer.lineCap = kCALineCapRound; shapeLayer.path = path.CGPath; //add it to our view [self.containerView.layer addSublayer:shapeLayer]; }
隐式动画
Core Animation基于一个假设,说屏幕上的任何东西都是可以(或可能) 做动画。
动画并不需要你在Core Animation中手动打开,相反需要明确的关闭,否者它一只存在。
当你改变CALayer的一个可以做动画的属性,它并不能在屏幕上体现出来。相反,它是从先前的值平滑过渡到新的值。这一切都是默认的行为,你不需要做额外的操作。
? 一个View上放一个CALayer,点击按钮改变layer的颜色<颜色是渐变过去的>
这就是隐式动画,之所以叫隐式动画是因为我们并没有指定任何的动画类型。我们仅仅改变了一个属性,然后Core Animation来决定如何并且何时去动画。
CATransaction 管理事务的类
+begin 入栈
+commit 出栈
+setAnimationDuration: 设置当前动画的时间
+AnimationDuration 获取动画时间
Core Animation在每个RUNLOOP周期中自动开始一次新的事物。
RUNROOP:iOS中负责收集用户输入,处理定时器或者网络事件并且重新绘制屏幕的东西。理解:涉及到任何主动或被动的操作改变都会唤醒run loop。
即使不显式的用[CATransaction begin]开始一次事务,任何在一次run loop循环中属性的改变都会被集中起来,然后做一次0.25秒的动画。
UIView中的layer属性 并不存在 隐形动画
总结
1.UIView关联的图层禁用了隐形动画,对这种图层做动画的唯一方法就是使用IUView的动画函数(而不是依赖CATransaction),或者继承UIView,并覆盖 -actionForLayer:forKey: 方法,或者直接创建一个显性动画。
2.对于独立的图层,我们可以通过实现图层的 -actionForLayer:forKey:委托方法,或者提供一个actions字典来控制隐形动画
显式动画
animationDidStop: finished:
代码demo
属性动画
基础动画
CABasicAnimation *animation = [CABasicAnimation animation]; animation.keyPath = @"backgroundColor";animation.toValue = (__bridge id)color.CGColor;animation.delegate = self;//apply animation to layer[self.colorLayer addAnimation:animation forKey:nil];
关键帧路径动画
CAKeyframeAnimation *animation = [CAKeyframeAnimation animation]; animation.keyPath = @"position"; animation.duration = 4.0; animation.path = bezierPath.CGPath; animation.rotationMode = kCAAnimationRotateAuto; [shipLayer addAnimation:animation forKey:nil];
动画组
CABasicAnimation和CAKeyframeAnimation仅仅是作用于单独的属性。
CAAnimationGroup可以把这些动画组合在一起 <CAAnimation 子类>
过渡
属性动画只对图层的可动画属性属性起作用
所以如果要改变一个不能动画的属性(比如图片),或者从层级关系中添加或者移除图层,属性动画将不起作用。
过渡:过渡并不像属性动画那样平滑的在两个值之间做动画,而是影响到整个图层的变化
。过渡动画首先展示之前的图层外观,然后通过一个交换过渡到新的外观。
CATransition:CAAnimation子类
CATransition有个type和subtype来识别变换效果,类型 NSString
type kCATransitionFade //默认 淡入淡出效果 kCATransitionMoveIn //新图片滑动进入,直接覆盖旧的图片 kCATransitionPush //边缘的一侧进来,把旧的图层从另一侧推出去 kCATransitionReveal //旧图片滑出去,来显示新图片 subtype kCATransitionFromRight kCATransitionFromLeft kCATransitionFromTop kCATransitionFromBottom
在想:控制器之间的过场动画是不是能用这个实现。
仿射变换
UIView可以通过transform属性做变换,transform是一个CGAffineTransform类型
CGAffineTransform 是用矩阵相乘的方法实现仿射变换
CGAffineTransformMakeRotation(CGFloat angle) 旋转
CGAffineTransformMakeScale(CGFloat sx, CGFloat sy) 缩放
CGAffineTransformMakeTranslation(CGFloat tx, CGFloat ty) 位移
CALayer 同样又个transform属性,transform的属性是CATransform3D
CALayer对应UIView的transform属性叫做affineTransform
混合变换
当操纵一个变换的时候,初始生成一个什么都不做的变换很重要-也就是创建一个CGAffineTransform类型的空值。<CGAffineTransformIdentity>
CGAffineTransformRotate(CGAffineTransform t, CGFloat angle) CGAffineTransformScale(CGAffineTransform t, CGFloat sx, CGFloat sy) CGAffineTransformTranslate(CGAffineTransform t, CGFloat tx, CGFloat ty)
最后,如果需要混合两个已经存在的矩阵,就可以使用下面方法,在两个基础上新建一个变换
CGAffineTransformConcat(CGAffineTransform t1, CGAffineTransform t2);
先缩小50%-再旋转30度-最后向右移动200像素
- (void)viewDidLoad { [super viewDidLoad]; //create a new transform CGAffineTransform transform = CGAffineTransformIdentity; //scale by 50%transform = CGAffineTransformScale(transform, 0.5, 0.5); //rotate by 30 degreestransform = CGAffineTransformRotate(transform, M_PI / 180.0 * 30.0); //translate by 200 points transform = CGAffineTransformTranslate(transform, 200, 0);//apply transform to layer self.layerView.layer.affineTransform = transform; }
结果并不是预期的结果:这意味着变换的顺序会影响最终的结果,也就是说旋转之后的平移和平移之后的的旋转结果可能不同
CG - Core Graphics 框架 :严格意义上说是2D绘图API
CGAffineTransform 仅仅对2D变换有效
3D变换
CALayer的transform属性是CATransform3D类型,就能实现3D空间内移动或者旋转
CATransform3DMakeRotation(CGFloat angle, CGFloat x, CGFloat y, CGFloat z)
CATransform3DMakeScale(CGFloat sx, CGFloat sy, CGFloat sz) CATransform3DMakeTranslation(Gloat tx, CGFloat ty, CGFloat tz)
想象x、y、z向旋转,缩放、位移的效果
绕Y轴做45度角的旋转
- (void)viewDidLoad { [super viewDidLoad]; //rotate the layer 45 degrees along the Y axis CATransform3D transform = CATransform3DMakeRotation(M_PI_4, 0, 1, 0); self.layerView.layer.transform = transform; }
sublayerTransform:CALayer的属性,可以对所有子视图操作