前言
QeartzCore是iOS中的图层框架,Quartz Core 的渲染能力可以像三维一样对二维图像进行任意操纵,在这个框架中我们可以对试图的图层进行定制,以实现我们想要的效果。
下面看一下这个框架中的类文件有哪些:
CALayer就是QeartzCore框架中的一个类,CALayer是个与UIView很类似的概念,同样有backgroundColor、frame等相似的属性,我们可以将UIView看做一种特殊的CALayer。但实际上UIView是对CALayer封装,在CALayer的基础上再添加交互功能。UIView的显示必须依赖于CALayer。我们同样可以跟新建view一样新建一个layer,然后添加到某个已有的layer上,同样可以对layer调整大小、位置、透明度等。一般来说,layer可以有两种用途:一是对view相关属性的设置,包括圆角、阴影、边框等参数,更详细的参数请点击这里;二是实现对view的动画操控。因此对一个view进行动画,本质上是对该view的.layer进行动画操纵。CALayer的设计主要是了为了内容展示和动画操作,CALayer本身并不包含在UIKit中,它不能响应事件。
一个UIView上的图层关系大概是这样的:
CALayer常用的属性
CALayer 最常用的两个子类:
-
CAGradientLayer(用于颜色渐变的实现)
// 创建 UIView 用来承载渐变色 UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 50, 200)]; [self.view addSubview:myView]; // 创建 CAGradientLayer 对象 CAGradientLayer *gradientLayer = [CAGradientLayer layer]; // 设置 gradientLayer 的 Frame gradientLayer.frame = myView.bounds; // 创建渐变色数组,需要转换为CGColor颜色 gradientLayer.colors = @[(id)[UIColor redColor].CGColor, (id)[UIColor yellowColor].CGColor, (id)[UIColor blueColor].CGColor]; #// 设置三种颜色变化点,取值范围 0.0~1.0 gradientLayer.locations = @[@(0.1f) ,@(0.4f)]; #// 设置渐变颜色方向,左上点为(0,0), 右下点为(1,1) gradientLayer.startPoint = CGPointMake(0, 0); gradientLayer.endPoint = CGPointMake(0, 1); // 添加渐变色到创建的 UIView 上去 [myView.layer addSublayer:gradientLayer];
其中:fillMode主要是决定显示layer在动画完成后的状态..一般和removedOnCompletion一起使用..
如果fillmode是..kCAFillModeRemoved 或..kCAFillModeBackwards...
不管removedOnCompletion是yes还是no,都会回到原始状态..一般用在重复的动画里..比如图片旋转5圈..你做一圈的功能.然后重复5次..就行了..
kCAFillModeForwards 或 kCAFillModeBoth模式下...如果..removedOnCompletion 是yes,动画完成后会回到原始状态..removedOnCompletion是NO的话..动画完成后会保持状态..保持状态只是保持可见层(presentation)的状态...layer本身的状态不会改变.
CAShapeLayer
CAShapeLayer顾名思义,继承于CALayer。 每个CAShapeLayer对象都代表着将要被渲染到屏幕上的一个任意的形状(shape)。具体的形状由其path(类型为CGPathRef)属性指定。 普通的CALayer是矩形,所以需要frame属性。CAShapeLayer初始化时也需要指定frame值(也可以不指定,只要path路径设置正确就行),但它本身没有形状,它的形状来源于其属性path 。CAShapeLayer有不同于CALayer的属性,它从CALayer继承而来的属性在绘制时是不起作用的。
在使用Core Animation开发动画的本质就是将CALayer中的内容转化为位图从而供硬件操作。
CAShapeLayer有着几点很重要:
- 它依附于一个给定的path,必须给与path,而且,即使path不完整也会自动首尾相接
- strokeStart以及strokeEnd代表着在这个path中首部、尾部所占整个路径的百分比位置,strokeEnd 需要大于 strokeStart,控制这俩值的大小,结合CABasicAnimation 即可实现 CAShapeLayer的绘图动画。
- CAShapeLayer动画仅仅限于沿着边缘的动画效果,它实现不了填充效果
我们可以使用CAShapeLayer与UIBezierPath可以实现不在view的drawRect方法中就画出一些想要的图形。大致步骤如下:
1、新建UIBezierPath对象bezierPath
2、新建CAShapeLayer对象caShapeLayer
3、将bezierPath的CGPath赋值给caShapeLayer的path,即caShapeLayer.path = bezierPath.CGPath
4、把caShapeLayer添加到某个显示该图形的layer中
#值得注意的是,CAShapeLayer 在初始化的时候可以不指定 Frame,其位置和形状由UIBezierPath决定。
#比如末端是矩形还是圆形,都是 UIBezierPath的设置,而且fillColor 也是 UIBezierPath区域内的颜色。LineWidth 是在边界上绘制的宽度,而且 绘制的宽度被边界一分为二。
#如果LineWidth 为0 ,strokeColor设置后也是没有效果的。
这面这个例子就是使用 CAShapeLayer与UIBezierPath以及CABasicAnimation结合在一起,实现的动态画图。
#核心实现代码
//头
CAShapeLayer *headLayer = [CAShapeLayer layer];
UIBezierPath *headPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(self.view.frame.size.width/2-80, 0, 160, 160) cornerRadius:80];
[self setLayer:headLayer path:headPath delay:delay*0];
- (void)setLayer:(CAShapeLayer *)layer path:(UIBezierPath *)path delay:(CFTimeInterval)delay
{
layer.path = path.CGPath;
layer.fillColor = [UIColor clearColor].CGColor;
layer.strokeColor = [UIColor lightGrayColor].CGColor;
__weak typeof(self) weakSelf = self;
# 由代码可知,动画的先后执行时间顺序是通过 dispatch_after 实现的。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakSelf.displayView.layer addSublayer:layer];
[weakSelf addAnimation:layer duration:LanPangZiDuration];
});
}
- (void)addAnimation:(CAShapeLayer *)layer duration:(CFTimeInterval)duration
{
switch (_animationType) {
case AnimationTypeNone:
break;
case AnimationTypeOne:
[self addAnimationOneOnLayer:layer duration:duration];
break;
case AnimationTypeTwo:
[self addAnimationTwoOnLayer:layer duration:duration];
break;
case AnimationTypeThree:
[self addAnimationThreeOnLayer:layer duration:duration];
break;
default:
break;
}
}
#pragma mark - 利用layer的 strokeEnd、strokeStart和lineWidth 属性添加CA动画
- (void)addAnimationOneOnLayer:(CAShapeLayer *)layer duration:(CFTimeInterval)duration
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
animation.fromValue = @(0);
animation.toValue = @(1);
animation.duration = duration;
[layer addAnimation:animation forKey:nil];
}
核心动画
下面是核心动画的几个类:
下面我们从上图的协议以及类的属性入手,分析一下上图结构:
-
CAMediaTiming 协议中定义了时间,速度,重复次数等。
属性定义如下: beginTime -> 用来设置动画延时,若想延迟1秒,就设置为CACurrentMediaTime()+1,其中CACurrentMediaTime()为图层当前时间。 duration -> 动画的持续时间。 speed -> 动画速率,决定动画时间的倍率。当speed为2时,动画时间为设置的duration的1/2。 timeOffset -> 动画时间偏移量。比如设置动画时长为3秒,当设置timeOffset为1.5时,当前动画会从中间位置开始,并在到达指定位置时,走完之前跳过的前半段动画。 repeatCount -> 动画的重复次数。 repeatDuration -> 动画的重复时间。 autoreverses -> 动画由初始值到最终值后,是否反过来回到初始值的动画。如果设置为YES,就意味着动画完成后会以动画的形式回到初始值。 fillMode -> 决定当前对象在非动画时间段的行为.比如动画开始之前,动画结束之后。 其实不只是CAAnimation遵循CAMediaTiming协议,熟悉底层结构的小伙伴们应该知道CALayer也遵循这个协议,所有在一定程度上我们可以通过控制layer本身的协议属性来控制动画节奏。
-
CAAnimation 核心动画基础类,不能直接使用。除了CAMediaTiming协议中的方法,增加了CAAnimationDelegate的代理属性等。
具体如下: timingFunction -> 控制动画的节奏。 系统提供的包括:kCAMediaTimingFunctionLinear (匀速), kCAMediaTimingFunctionEaseIn (慢进快出), kCAMediaTimingFunctionEaseOut (快进慢出), kCAMediaTimingFunctionEaseInEaseOut (慢进慢出,中间加速), kCAMediaTimingFunctionDefault (默认), 当然也可通过自定义创建CAMediaTimingFunction。 delegate -> 代理。 removedOnCompletion -> 是否让图层保持显示动画执行后的状态,默认为YES,也就是动画执行完毕后从涂层上移除,恢复到执行前的状态,如果设置为NO,并且设置fillMode为kCAFillModeForwards,则保持动画执行后的状态。
-
CATransition 转场动画,系统提供了很多酷炫效果。
属性如下: type -> 转场动画类型。 subtype -> 转场动画方向。 startProgress -> 动画起点进度(整体的百分比)。 endProgress -> 动画终点进度(整体的百分比)。 filter -> 自定义转场。
-
CAAnimationGroup
顾名思义,这是一个动画组,它允许多个动画组合在一起并行显示.比如这里设置了两个动画, 把他们加在动画组里,一起显示.例如你有几个动画,在动画执行的过程中需要同时修改动画的某些属性,这时候就可以使用CAAnimationGroup. duration 动画持续时间,值得一提的是,如果添加到group里的子动画不设置此属性,group里的duration会统一设置动画(包括子动画)的duration属性;但是如果子动画设置了duration属性,那么group的duration属性的值不应该小于每个子动画中duration属性的值,否则会造成子动画显示不全就停止了动画. autoreverses 动画完成后自动重新开始,默认为NO. repeatCount 动画重复次数,默认为0. animations 动画组(数组类型),把需要同时运行的动画加到这个数组里. addAnimation:forKey 这个方法的forKey参数是一个字符串,这个字符串可以随意设 如果你需要在动画group执行结束后保存动画效果的话,设置 fillMode 属性,并且把 removedOnCompletion 设置为NO;
-
CAPropertyAnimation 属性动画,针对对象的可动画属性进行效果的设置,不可直接使用。
添加属性具体如下: keyPath -> CALayer的某个属性名,并通过这个属性的值进行修改,达到相应的动画效果。 additive -> 属性动画是否以当前动画效果为基础,默认为NO。 cumulative -> 指定动画是否为累加效果,默认为NO。 valueFunction -> 此属性配合CALayer的transform属性使用。
-
CAKeyframeAnimation 关键帧动画,同样通过keyPath对应属性进行控制,但它可以通过values或者path进行多个阶段的控制。CAKeyframeAnimation是CApropertyAnimation的子类,跟CABasicAnimation的区别是:CABasicAnimation只能从一个数值(fromValue)变到另一个数值(toValue),而CAKeyframeAnimation会使用一个NSArray保存这些数值。
属性如下: values -> 关键帧组成的数组,动画会依次显示其中的每一帧。 path -> 关键帧路径,动画进行的要素,优先级比values高,但是只对CALayer的anchorPoint和position起作用。 keyTimes -> 每一帧对应的时间,如果不设置,则各关键帧平分设定时间。 timingFunctions -> 每一帧对应的动画节奏。 calculationMode -> 动画的计算模式,系统提供了对应的几种模式。 tensionValues -> 动画张力控制。 continuityValues -> 动画连续性控制。 biasValues -> 动画偏差率控制。 rotationMode -> 动画沿路径旋转方式,系统提供了两种模式。
-
CABasicAnimation基础动画,通过keyPath对应属性进行控制,需要设置fromValue以及toValue。 CABasicAnimation提供了最基础的动画属性设置,是简单的keyframe动画性能。
CABasicAnimation可以看做是一种CAKeyframeAnimation的简单动画,因为它只有头尾的关键帧(keyframe)。添加属性如下: fromValue -> keyPath相应属性的初始值。 toValue -> keyPath相应属性的结束值。 byValue -> 在不设置toValue时, toValue = fromValue + byValue,也就是在当前的位置上增加多少。
关于核心动画里面的时间暂停,继续的问题可以看我另一篇文章:实践-跑马灯效果及实现过程解析
-
CASpringAnimation 带有初始速度以及阻尼指数等物理参数的属性动画。我们可以把它看成在不绝对光滑的地面上,一个弹簧拴着别小球,那么我们可以这么理解他的属性:
mass -> 小球质量,影响惯性。 stiffness -> 弹簧的劲度系数。 damping -> 阻尼系数,地面的摩擦力。 initialVelocity -> 初始速度,相当于给小球一个初始速度(可正可负,方向不同) settlingDuration -> 结算时间,根据上述参数计算出的预计时间,相对于你设置的时间,这个时间比较准确。
属性解析:
- values:就是上述的NSArray对象。里面的元素称为”关键帧”(keyframe)。动画对象会在指定的时间(duration)内,依次显示values数组中的每一个关键帧
- path:可以设置一个CGPathRef\CGMutablePathRef,让层跟着路径移动。path只对CALayer的anchorPoint和position起作用。如果你设置了path,那么values将被忽略
- keyTimes:可以为对应的关键帧指定对应的时间点,其取值范围为0到1.0,keyTimes中的每一个时间值都对应values中的每一帧.当keyTimes没有设置的时候,各个关键帧的时间是平分的
calculationMode:
const kCAAnimationLinear//线性,默认
const kCAAnimationDiscrete//离散,无中间过程(没有中间圆滑的过渡),但keyTimes设置的时间依旧生效,物体跳跃地出现在各个关键帧上
const kCAAnimationPaced//平均,keyTimes跟timeFunctions失效
const kCAAnimationCubic//平均,同上
const kCAAnimationCubicPaced//平均,同上
便利构造函数 animationWithKeyPath: KeyPath需要一个字符串类型的参数,实际上是一个 键-值编码协议的扩展,
参数必须是CALayer的某一项属性,你的代码会对应的去改变该属性的效果 具体可以填写什么请参考上面的URL,切勿乱填!
例如这里填写的是 @"transform.rotation.z" 意思就是围绕z轴旋转,旋转的单位是弧度.这个动画的效果是把view旋转到最小,再旋转回来.你也可以填写@"opacity" 去修改透明度...以此类推.修改layer的属性,可以用这个类.
toValue 动画结束的值.
CABasicAnimation自己只有三个属性(都很重要)(其他属性是继承来的),分别为:
fromValue(开始值),
toValue(结束值),
byValue(偏移值), 这三个属性最多只能同时设置两个;
他们之间的关系如下:
如果同时设置了fromValue和toValue,那么动画就会从fromValue过渡到toValue;
如果同时设置了fromValue和byValue,那么动画就会从fromValue过渡到fromValue + byValue;
如果同时设置了byValue 和toValue,那么动画就会从toValue - byValue过渡到toValue;
如果只设置了fromValue,那么动画就会从fromValue过渡到当前的value;
如果只设置了toValue ,那么动画就会从当前的value过渡到toValue;
如果只设置了byValue ,那么动画就会从从当前的value过渡到当前value + byValue.
可以这么理解,当你设置了三个中的一个或多个,系统就会根据以上规则使用插值算法计算出一个时间差并同时开启一个Timer.Timer的间隔也就是这个时间差,通过这个Timer去不停地刷新keyPath的值.而实际上,keyPath的值(layer的属性)在动画运行这一过程中,是没有任何变化的,它只是调用了GPU去完成这些显示效果而已. 在这个动画里,是设置了要旋转到的弧度,根据以上规则,动画将会从它当前的弧度专旋转到我设置的弧度.
duration 动画持续时间
timingFunction 动画起点和终点之间的插值计算,也就是说它决定了动画运行的节奏,是快还是慢,还是先快后慢...
一些常用的animationWithKeyPath值的总结
#都是 CAShapeLayer 或者 CALayer 的属性
strokeStart #一条Path的起始
strokeEnd #一条Path的终止的位置
transform.scale 比例转化 @(0.8)
transform.scale.x 宽的比例 @(0.8)
transform.scale.y 高的比例 @(0.8)
transform.translation.x 往x轴方向移动
transform.translation.y 往y轴方向移动
transform.rotation.x 围绕x轴旋转 @(M_PI)
transform.rotation.y 围绕y轴旋转 @(M_PI)
transform.rotation.z 围绕z轴旋转 @(M_PI)
cornerRadius 圆角的设置 @(50)
backgroundColor 背景颜色的变化 (id)[UIColor purpleColor].CGColor
bounds 大小,中心不变 [NSValue valueWithCGRect:CGRectMake(0, 0, 200, 200)];
position 位置(中心点的改变) [NSValue valueWithCGPoint:CGPointMake(300, 300)];
contents 内容,比如UIImageView的图片 imageAnima.toValue = (id)[UIImage imageNamed:@"to"].CGImage;
opacity 透明度 @(0.7)
contentsRect.size.width 横向拉伸缩放 @(0.4)最好是0~1之间的
其他很不错的文章:
iOS动画篇_CoreAnimation(超详细解析核心动画)
CoreAnimation编程指南
谈谈iOS Animation
原IOS开发UI篇--IOS动画(Core Animation)总结
本文考虑到简洁就不上代码了,具体的代码实现的效果可以到我GitHub去下载查看,喜欢的话,请star 一下。