iOS动画篇_CALayer这些牛逼的子类你造吗

之前说到贝塞尔曲线,今天说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时:

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中现实的几种颜色并完成完美过渡,和CAShapeLayerpath一样,colorsCAGradientLayer特殊属性的起点,也就是x 显示的要素。

locations

颜色区间分布比例,默认为线性均匀分布。取值范围为0~1递增,一般来说其中的元素个数应与colors中的元素个数相同,不同时系统会自行处理分布规则。
设置 gradientLayer.locations = @[@(0.3),@(0.7)];

startPoint endPoint

startPoint决定了变色范围的起始点,endPoint决定了变色范围的结束点,两者的连线决定变色的趋势:

gradientLayer.startPoint = CGPointMake(0, 0); gradientLayer.endPoint = CGPointMake(1, 1);

gradientLayer.startPoint = CGPointMake(0.5, 0); gradientLayer.endPoint = CGPointMake(1, 1);
gradientLayer.startPoint = CGPointMake(0, 0.5); gradientLayer.endPoint = CGPointMake(1, 1);

gradientLayer.locations = @[@(0.3),@(0.7)]; // gradientLayer.startPoint = CGPointMake(0, 0); gradientLayer.endPoint = CGPointMake(0.5, 0.5);

注意:locations是相对于startPointendPoint的变化范围而言的。
看到这,你一定会说,这个玩意有什么卵用。

下面找一个之前写过的一个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这些牛逼的子类你造吗_补全篇

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容

  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,465评论 6 30
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 5,094评论 5 13
  • 目录: 主要绘图框架介绍 CALayer 绘图 贝塞尔曲线-UIBezierPath CALayer子类 补充:i...
    Ryan___阅读 1,658评论 1 9
  • Core Animation Core Animation,中文翻译为核心动画,它是一组非常强大的动画处理API,...
    45b645c5912e阅读 3,015评论 0 21
  • 有一天醒了,突发奇想做了一个问卷,我称之为<枝楼 · 奇迹问卷>。感谢这个回答认真的匿名者。 一、婴儿问卷【婴儿时...
    枝楼阅读 316评论 0 1