QeartzCore是iOS中的图层框架,Quartz Core 的渲染能力可以像三维一样对二维图像进行任意操纵,在这个框架中我们可以对试图的图层进行定制,以实现我们想要的效果。
下面看一下这个框架中的类文件有哪些:
layer.png
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上的图层关系大概是这样的:
Paste_Image.png
layer.png
CALayer 最常用的两个子类:
其中:fillMode主要是决定显示layer在动画完成后的状态..一般和removedOnCompletion一起使用.. 如果fillmode是..kCAFillModeRemoved 或..kCAFillModeBackwards... 不管removedOnCompletion是yes还是no,都会回到原始状态..一般用在重复的动画里..比如图片旋转5圈..你做一圈的功能.然后重复5次..就行了.. kCAFillModeForwards 或 kCAFillModeBoth模式下...如果..removedOnCompletion 是yes,动画完成后会回到原始状态..removedOnCompletion是NO的话..动画完成后会保持状态..保持状态只是保持可见层(presentation)的状态...layer本身的状态不会改变.
CAShapeLayer顾名思义,继承于CALayer。 每个CAShapeLayer对象都代表着将要被渲染到屏幕上的一个任意的形状(shape)。具体的形状由其path(类型为CGPathRef)属性指定。 普通的CALayer是矩形,所以需要frame属性。CAShapeLayer初始化时也需要指定frame值(也可以不指定,只要path路径设置正确就行),但它本身没有形状,它的形状来源于其属性path 。CAShapeLayer有不同于CALayer的属性,它从CALayer继承而来的属性在绘制时是不起作用的。
在使用Core Animation开发动画的本质就是将CALayer中的内容转化为位图从而供硬件操作。
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结合在一起,实现的动态画图。
动态绘图.gif
#核心实现代码
//头
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];
}
下面是核心动画的几个类:
下面我们从上图的协议以及类的属性入手,分析一下上图结构:
- 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 动画起点和终点之间的插值计算,也就是说它决定了动画运行的节奏,是快还是慢,还是先快后慢...
#都是 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(超详细解析核心动画)
效果.gif
本文考虑到简洁就不上代码了,具体的代码实现的效果可以到我GitHub去下载查看,喜欢的话,请star 一下。