之前说到贝塞尔曲线,今天说CALayer,哈哈哈,承上启下,放一个表情与上一篇相呼应。
CALayer
首先,CALayer其实自身有很多情况下自带隐式动画,但是需要注意的是UIView的根Layer是没有隐式动画的。下面看一下CALayer的几个重要的属性:
CALayer属性
对于大多数开发者老说,CALayer是很熟悉的一个名词,但是对于它的属性可能一知半解,上表大致说明了CALayer的几个重要属性的作用以及是否支持隐式动画,下面着重介绍几个点:
(1)frame属性
frame不支持隐式动画,所以通常使用bounds和position来设置layer的位置和大小
(2)contents
设置layer的contents可以为layer添加显示内容,最直接的便是将UIImage转化为CGImageRef赋值,其与backgroundColor的关系就相当于UIImageView的image和backgorundColor的关系。
(3)锚点 anchorPoint
锚点的取值范围为(0~1,0~1),标示此点相对于宽高的比例,默认点(0.5,0.5),锚点的位置对图层的位置以及某些动画的重心起到决定性的作用,通过锚点计算图层位置-->中心点相对于父父层的位置永远不变,锚点于中心点重合。以旋转动画为例,锚点是旋转的中心。
(4)mask
mask的作用就相当于PS中的蒙版,在一些图层处理上具有很大的优势(注:蒙版透明色为过滤掉指定layer的区域内容,不透明色为显示制定图层的区域内容)
CALayer的子类
CALayer子类图解CALayer的子类有很多,下面说几个比较常用的:
CAShapeLayer
看一下CAShapeLayer相对于CALayer多出来的属性:
path
CAShapeLayer的path属性是他如此牛逼的一个重要起点,也是它和贝塞尔曲线紧密连接一个入口,他决定了我们要在图层上画一个什么形状。(注:当与贝塞尔曲线一起使用的时候,生成的曲线的位置是相对于生成的layer的,所以当你利用贝塞尔曲线设置了path后,再设置layer的position和bounds你会感觉很奇怪,最简单的方式就是单纯利用贝塞尔曲线决定图层的位置和大小)
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
//shapeLayer.position = CGPointMake(100, 100);
//shapeLayer.bounds = CGRectMake(0, 0, 100, 100);
//shapeLayer.anchorPoint = CGPointMake(0, 0);
shapeLayer.path = bezierPath.CGPath;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor blueColor].CGColor;
shapeLayer.lineWidth = 10;
self.view.layer.mask = shapeLayer;
//[self.view.layer addSublayer:shapeLayer];
fillColor 和 fillRule
fillColor
即layer的path的填充颜色,fillRule
属性用于指定使用哪一种算法去判断画布上的某区域是否属于该图形“内部” (内部区域将被填充)。对一个简单的无交叉的路径,哪块区域是“内部” 是很直观清除的。但是,对一个复杂的路径,比如自相交或者一个子路径包围另一个子路径,“内部”的理解就不那么明了。
fillRule提供两种选项用于指定如何判断图形的内部”
kCAFillRuleNonZero
,kCAFillRuleEvenOdd
:
-(void)checkFillRule{
UIBezierPath *path = [[UIBezierPath alloc] init];
CGPoint circleCenter = self.view.center;
[path moveToPoint:CGPointMake(circleCenter.x + 50, circleCenter.y)];
[path addArcWithCenter:circleCenter radius:50 startAngle:0 endAngle:2*M_PI clockwise:YES];
[path moveToPoint:CGPointMake(circleCenter.x + 100, circleCenter.y)];
[path addArcWithCenter:circleCenter radius:100 startAngle:0 endAngle:2*M_PI clockwise:YES];
[path moveToPoint:CGPointMake(circleCenter.x + 150, circleCenter.y)];
[path addArcWithCenter:circleCenter radius:150 startAngle:0 endAngle:2*M_PI clockwise:YES];
//create shape layer
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.strokeColor = [UIColor redColor].CGColor;
shapeLayer.fillColor = [UIColor greenColor].CGColor;
shapeLayer.fillRule = kCAFillRuleNonZero;
//shapeLayer.fillRule = kCAFillRuleEvenOdd;
shapeLayer.lineWidth = 5;
shapeLayer.lineJoin = kCALineJoinBevel;
shapeLayer.lineCap = kCALineCapRound;
shapeLayer.path = path.CGPath;
//add it to our view
[self.view.layer addSublayer:shapeLayer];
}
当fillRule为kCAFillRuleNonZero
时:
当fillRule为
kCAFillRuleEvenOdd
时:通过上面的代码再加上下面抄袭的描述,应该能够相当清晰的理解这其中的意义。
kCAFillRuleNonZero
字面意思是“非零”。按该规则,要判断一个点是否在图形内,从该点作任意方向的一条射线,然后检测射线与图形路径的交点情况。从0开始计数,路径从左向右穿过射线则计数加1,从右向左穿过射线则计数减1。得出计数结果后,如果结果是0,则认为点在图形外部,否则认为在内部。下图演示了kCAFillRuleNonZero规则 :
kCAFillRuleEvenOdd
字面意思是“奇偶”。按该规则,要判断一个点是否在图形内,从该点作任意方向的一条射线,然后检测射线与图形路径的交点的数量。如果结果是奇数则认为点在内部,是偶数则认为点在外部。下图演示了kCAFillRuleEvenOdd 规则:
strokeColor
线颜色
strokeStart strokeEnd
两者的取值都是0~1,决定贝塞尔曲线的划线百分比,对应值的改变支持隐式动画。
UIBezierPath* bezierPath_rect = [UIBezierPath bezierPathWithRect:CGRectMake(30, 50, 100, 100)];
bezierPath_rect.lineWidth = 10;
CAShapeLayer* shapeLayer = [CAShapeLayer layer];
shapeLayer.path = bezierPath_rect.CGPath;
shapeLayer.fillColor = [UIColor redColor].CGColor;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.lineWidth = 10;
shapeLayer.strokeStart = 0;
shapeLayer.strokeEnd = 0.3;
[self.view.layer addSublayer:shapeLayer];
运行结果:
lineWidth
线宽
miterLimit
最大斜接长度,只有lineJoin属性为kCALineJoinMiter时miterLimit才有效,当衔接角度太小时,斜接长度就会很大,如果设置了 miterLimit
并且当斜接长度大于这个值时,便会对应裁切掉多余区域:
设置
miterLimit
后:lineCap
线端点类型,也就是对应曲线结束的点的显示样式。
lineJoin
连接点类型,也就是对应曲线节点的位置的显示样式。
lineDashPattern
虚线设置,为一个数组,数组中奇数位实线长度,偶数位带遍空白长度(注意:这里的奇数,偶数以数组的第一个元素索引为1计算)
lineDashPhase
虚线开始的位置,可以使用此属性做一个滚动的虚线框。
CAShapeLayer* dashLineShapeLayer = [CAShapeLayer layer];
//创建贝塞尔曲线
UIBezierPath* dashLinePath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, 200, 100) cornerRadius:20];
dashLineShapeLayer.path = dashLinePath.CGPath;
dashLineShapeLayer.position = CGPointMake(100, 100);
dashLineShapeLayer.fillColor = [UIColor clearColor].CGColor;
dashLineShapeLayer.strokeColor = [UIColor whiteColor].CGColor;
dashLineShapeLayer.lineWidth = 3;
dashLineShapeLayer.lineDashPattern = @[@(6),@(6)];
dashLineShapeLayer.strokeStart = 0;
dashLineShapeLayer.strokeEnd = 1;
dashLineShapeLayer.zPosition = 999;
//
[self.view.layer addSublayer:dashLineShapeLayer];
//
NSTimeInterval delayTime = 0.3f;
//定时器间隔时间
NSTimeInterval timeInterval = 0.1f;
//创建子线程队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//使用之前创建的队列来创建计时器
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//设置延时执行时间,delayTime为要延时的秒数
dispatch_time_t startDelayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayTime * NSEC_PER_SEC));
//设置计时器
dispatch_source_set_timer(_timer, startDelayTime, timeInterval * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(_timer, ^{
//执行事件
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
CGFloat _add = 3;
dashLineShapeLayer.lineDashPhase -= _add;
});
});
// 启动计时器
dispatch_resume(_timer);
效果如图:(视频好像有点快...)
其实CAShapeLayer的用处很多,能实现很多动画效果的同时,也可以实现很多蒙版效果,心有多大,功能就有多大。
之前写过一个点击扩散的库,主要用到蒙版mask以及shapeLayer和核心动画,您上眼:
//
// UIView+YSTransform.h
// JR_New_AnimationDemo
//
// Created by ys on 2016/3/20.
// Copyright © 2016年 ys. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface UIView (YSTransform)<CAAnimationDelegate>
-(void)YSTransform_circleColor_toColor:(UIColor*)toColor Duration:(CGFloat)duration StartPoint:(CGPoint)startPoint;
-(void)YSTransform_circleImage_toImage:(UIImage*)toImage Duration:(CGFloat)duration StartPoint:(CGPoint)startPoint;
-(void)YSTransForm_beginZoom_max:(CGFloat)max min:(CGFloat)min;
-(void)YSTransForm_StopZoom;
@end
//
// UIView+YSTransform.m
// JR_New_AnimationDemo
//
// Created by ys on 2016/3/20.
// Copyright © 2016年 ys. All rights reserved.
//
#import "UIView+YSTransform.h"
#import <objc/runtime.h>
@implementation UIView (YSTransform)
-(void)YSTransform_circleColor_toColor:(UIColor*)toColor Duration:(CGFloat)duration StartPoint:(CGPoint)startPoint{
CALayer *tempLayer = objc_getAssociatedObject(self, @"tempLayer");
if (!tempLayer) {
tempLayer = [[CALayer alloc] init];
tempLayer.bounds = self.bounds;
tempLayer.position = self.center;
tempLayer.backgroundColor = self.backgroundColor.CGColor;
[self.layer addSublayer:tempLayer];
objc_setAssociatedObject(self, @"tempLayer", tempLayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//
tempLayer.contents = nil;
tempLayer.backgroundColor = toColor.CGColor;
CGFloat screenHeight = self.frame.size.height;
CGFloat screenWidth = self.frame.size.width;
CGRect rect = CGRectMake(startPoint.x, startPoint.y, 2, 2);
UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:rect];
UIBezierPath *endPath = [UIBezierPath bezierPathWithArcCenter:startPoint radius:sqrt(screenHeight * screenHeight + screenWidth * screenWidth) startAngle:0 endAngle:M_PI*2 clockwise:YES];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = endPath.CGPath;
tempLayer.mask = maskLayer;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.delegate = self;
animation.fromValue = (__bridge id)(startPath.CGPath);
animation.toValue = (__bridge id)((endPath.CGPath));
animation.duration = 1;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[animation setValue:@"YSCircleColor_value" forKey:@"YSCircleColor_key"];
[maskLayer addAnimation:animation forKey:@"YSCircleColor"];
}
-(void)YSTransform_circleImage_toImage:(UIImage*)toImage Duration:(CGFloat)duration StartPoint:(CGPoint)startPoint{
CALayer *tempLayer = objc_getAssociatedObject(self, @"tempLayer");
if (!tempLayer) {
tempLayer = [[CALayer alloc] init];
tempLayer.bounds = self.bounds;
tempLayer.position = self.center;
tempLayer.backgroundColor = self.backgroundColor.CGColor;
[self.layer addSublayer:tempLayer];
objc_setAssociatedObject(self, @"tempLayer", tempLayer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
tempLayer.contents = (id)toImage.CGImage;
//
CGFloat screenHeight = self.frame.size.height;
CGFloat screenWidth = self.frame.size.width;
CGRect rect = CGRectMake(startPoint.x, startPoint.y, 2, 2);
UIBezierPath *startPath = [UIBezierPath bezierPathWithOvalInRect:rect];
UIBezierPath *endPath = [UIBezierPath bezierPathWithArcCenter:startPoint radius:sqrt(screenHeight * screenHeight + screenWidth * screenWidth) startAngle:0 endAngle:M_PI*2 clockwise:YES];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.path = endPath.CGPath;
tempLayer.mask = maskLayer;
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.delegate = self;
animation.fromValue = (__bridge id)(startPath.CGPath);
animation.toValue = (__bridge id)((endPath.CGPath));
animation.duration = 1;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
[animation setValue:@"YSCircleImage_value" forKey:@"YSCircleImage_key"];
[maskLayer addAnimation:animation forKey:@"YSCircleImage"];
}
//
-(void)YSTransForm_beginZoom_max:(CGFloat)max min:(CGFloat)min{
[UIView animateWithDuration:0.3 animations:^{
self.transform = CGAffineTransformMakeScale(max, max);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.3 animations:^{
self.transform = CGAffineTransformMakeScale(min, min);
} completion:^(BOOL finished) {
NSNumber *nextStop = objc_getAssociatedObject(self, @"nextAniStop");
if ([nextStop boolValue]) {
[UIView animateWithDuration:0.3 animations:^{
self.transform = CGAffineTransformMakeScale(1, 1);
} completion:^(BOOL finished) {
self.transform = CGAffineTransformMakeScale(1, 1);
objc_setAssociatedObject(self, @"nextAniStop", @(0), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}];
}else{
[self YSTransForm_beginZoom_max:max min:min];
}
}];
}];
}
-(void)YSTransForm_StopZoom{
objc_setAssociatedObject(self, @"nextAniStop", @(1), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
//
-(void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
if (flag) {
CALayer *tempLayer = objc_getAssociatedObject(self, @"tempLayer");
if ([anim valueForKey:@"YSCircleColor_key"]) {
self.layer.contents = nil;
self.backgroundColor = [UIColor colorWithCGColor:tempLayer.backgroundColor];
}else if ([anim valueForKey:@"YSCircleImage_key"]){
self.layer.contents = tempLayer.contents;
}
}
}
@end
效果如下:
另外,其实我们看到的很多酷炫的变幻莫测的动画很多都是CAShapeLayer
或者Quartz 2D
绘图配合帧率刷新的结果。来,有时间我给你讲讲啊。
CAGradientLayer
下面再说一个比较常用的子类CAGradientLayer
,我们一般使用它生成平滑的颜色过渡。大致的效果如下:
同样,我们还是从它的特有属性入手:
colors
在Layer中现实的几种颜色并完成完美过渡,和CAShapeLayer
的path
一样,colors
是CAGradientLayer
特殊属性的起点,也就是x 显示的要素。
locations
颜色区间分布比例,默认为线性均匀分布。取值范围为0~1递增,一般来说其中的元素个数应与colors中的元素个数相同,不同时系统会自行处理分布规则。
设置 gradientLayer.locations = @[@(0.3),@(0.7)];
startPoint endPoint
startPoint
决定了变色范围的起始点,endPoint
决定了变色范围的结束点,两者的连线决定变色的趋势:
注意:
locations
是相对于startPoint
和endPoint
的变化范围而言的。看到这,你一定会说,这个玩意有什么卵用。
下面找一个之前写过的一个Demo,简单的彩色进度条:
//CAGradientLayer
-(void)creatGradientLayer{
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.bounds = CGRectMake(0, 0, 220, 220);
gradientLayer.position = self.view.center;
[self.view.layer addSublayer:gradientLayer];
//
NSMutableArray *colorArray = [NSMutableArray new];
for (NSInteger hue = 0; hue <= 360; hue += 5) {
UIColor *color = [UIColor colorWithHue:1.0*hue/360.0
saturation:1.0
brightness:1.0
alpha:1.0];
[colorArray addObject:(id)color.CGColor];
}
//
gradientLayer.colors = colorArray;
//gradientLayer.locations = @[@(0.3),@(0.7)];
//
gradientLayer.startPoint = CGPointMake(0, 0);
gradientLayer.endPoint = CGPointMake(1, 1);
//ShapeLayer
UIBezierPath *bezierP = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(10, 10, 200, 200)];
CAShapeLayer* shapeLayer = [[CAShapeLayer alloc] init];
shapeLayer.path = bezierP.CGPath;
shapeLayer.lineWidth = 10;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.strokeColor = [UIColor blueColor].CGColor;
shapeLayer.lineCap = kCALineCapRound;
shapeLayer.strokeStart = 0;
shapeLayer.strokeEnd = 0;
gradientLayer.mask = shapeLayer;
//
NSTimeInterval delayTime = 1.0f;
NSTimeInterval timeInterval = 0.5f;
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_time_t startDelayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayTime * NSEC_PER_SEC));
dispatch_source_set_timer(_timer, startDelayTime, timeInterval * NSEC_PER_SEC, 0.1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(_timer, ^{
dispatch_async(dispatch_get_main_queue(), ^{
if (shapeLayer.strokeEnd < 0.6) {
shapeLayer.strokeEnd += 0.4;
}else if (shapeLayer.strokeEnd < 0.8){
shapeLayer.strokeEnd += 0.2;
}else if (shapeLayer.strokeEnd < 1){
shapeLayer.strokeEnd += 0.1;
}else{
dispatch_source_cancel(_timer);
}
});
});
dispatch_resume(_timer);
}
运行结果
CAEmitterLayer
CAEmitterLayer,他的属性真的是看的让人生无可恋,就算你这个字段什么意思,写出你想要的效果也需要一个大量的调整的过程,简直爆炸。之前写了一个雨夹雪,供大家参考。
#pragma mark CAEmitterLayer && CAEmitterCell
/*
CAEmitterLayer 属性介绍
birthRate:粒子产生系数,默认1.0;
emitterCells: 装着CAEmitterCell对象的数组,被用于把粒子投放到layer上;
emitterDepth:决定粒子形状的深度联系:emittershape
emitterMode:发射模式
NSString * const kCAEmitterLayerPoints;
NSString * const kCAEmitterLayerOutline;
NSString * const kCAEmitterLayerSurface;
NSString * const kCAEmitterLayerVolume;
emitterPosition:发射位置
emitterShape:发射源的形状:
NSString * const kCAEmitterLayerPoint;
NSString * const kCAEmitterLayerLine;
NSString * const kCAEmitterLayerRectangle;
NSString * const kCAEmitterLayerCuboid;
NSString * const kCAEmitterLayerCircle;
NSString * const kCAEmitterLayerSphere;
emitterSize:发射源的尺寸大;
emitterZposition:发射源的z坐标位置;
lifetime:粒子生命周期
preservesDepth:不是多很清楚(粒子是平展在层上)
renderMode:渲染模式:
NSString * const kCAEmitterLayerUnordered;
NSString * const kCAEmitterLayerOldestFirst;
NSString * const kCAEmitterLayerOldestLast;
NSString * const kCAEmitterLayerBackToFront;
NSString * const kCAEmitterLayerAdditive;
scale:粒子的缩放比例:
seed:用于初始化随机数产生的种子
spin:自旋转速度
velocity:粒子速度
*/
- (void)startCAEmitterLayer {
// EmitterLayer
CGRect showRect = self.view.bounds;
UIView *view = [[UIView alloc] initWithFrame:showRect];
view.backgroundColor = [UIColor blackColor];
[self.view addSubview:view];
self.emitterLayer = [CAEmitterLayer layer];
self.emitterLayer.frame = view.bounds;
self.emitterLayer.masksToBounds = YES;
self.emitterLayer.emitterShape = kCAEmitterLayerLine;
self.emitterLayer.emitterMode = kCAEmitterLayerSurface;
self.emitterLayer.emitterSize = showRect.size;
self.emitterLayer.emitterPosition = CGPointMake(showRect.size.width / 2.f, - 20);
[self setEmitterCell];
[view.layer addSublayer:self.emitterLayer];
}
/*
CAEmitterCell 属性介绍
CAEmitterCell类代表从CAEmitterLayer射出的粒子;emitter cell定义了粒子发射的方向。
alphaRange: 一个粒子的颜色alpha能改变的范围;
alphaSpeed:粒子透明度在生命周期内的改变速度;
birthrate:粒子参数的速度乘数因子;每秒发射的粒子数量
blueRange:一个粒子的颜色blue 能改变的范围;
blueSpeed: 粒子blue在生命周期内的改变速度;
color:粒子的颜色
contents:是个CGImageRef的对象,既粒子要展现的图片;
contentsRect:应该画在contents里的子rectangle:
emissionLatitude:发射的z轴方向的角度
emissionLongitude:x-y平面的发射方向
emissionRange;周围发射角度
emitterCells:粒子发射的粒子
enabled:粒子是否被渲染
greenrange: 一个粒子的颜色green 能改变的范围;
greenSpeed: 粒子green在生命周期内的改变速度;
lifetime:生命周期
lifetimeRange:生命周期范围 lifetime= lifetime(+/-) lifetimeRange
magnificationFilter:不是很清楚好像增加自己的大小
minificatonFilter:减小自己的大小
minificationFilterBias:减小大小的因子
name:粒子的名字
redRange:一个粒子的颜色red 能改变的范围;
redSpeed; 粒子red在生命周期内的改变速度;
scale:缩放比例:
scaleRange:缩放比例范围;
scaleSpeed:缩放比例速度:
spin:子旋转角度
spinrange:子旋转角度范围
style:不是很清楚:
velocity:速度
velocityRange:速度范围
xAcceleration:粒子x方向的加速度分量
yAcceleration:粒子y方向的加速度分量
zAcceleration:粒子z方向的加速度分量
emitterCells:粒子发射的粒子
注意:粒子同样有emitterCells属性,也就是说粒子同样可以发射粒子。
*/
- (void)setEmitterCell {
CAEmitterCell *rainflake = [CAEmitterCell emitterCell];
rainflake.birthRate = 5.f;
rainflake.speed = 10.f;
rainflake.velocity = 10.f;
rainflake.velocityRange = 10.f;
rainflake.yAcceleration = 1000.f;
rainflake.contents = (__bridge id)([UIImage imageNamed:@"rain.png"].CGImage);
rainflake.color = [UIColor whiteColor].CGColor;
rainflake.lifetime = 160.f;
rainflake.scale = 0.2f;
rainflake.scaleRange = 0.f;
CAEmitterCell *snowflake = [CAEmitterCell emitterCell];
snowflake.birthRate = 1.f;
snowflake.speed = 10.f;
snowflake.velocity = 2.f;
snowflake.velocityRange = 10.f;
snowflake.yAcceleration = 10.f;
snowflake.emissionRange = 0.5 * M_PI;
snowflake.spinRange = 0.25 * M_PI;
snowflake.contents = (__bridge id)([UIImage imageNamed:@"snow.png"].CGImage);
snowflake.color = [UIColor cyanColor].CGColor;
snowflake.lifetime = 160.f;
snowflake.scale = 0.5;
snowflake.scaleRange = 0.3;
//添加到EmitterLayer中
self.emitterLayer.emitterCells = @[snowflake,rainflake];
}
运行效果:
剩下的那些CALayer的子类,正好我有空,如果你也有空,来这里,我说,你听,CALayer这些牛逼的子类你造吗_补全篇。
网友评论