《iOS核心动画高级技巧》笔记(四) - 专用图层

专用图层

简书很多标签不支持,以下目录跳转没用!!!

<a id="markdown-one"></a>CAShapeLayer

  • CAShapeLayer属性是CGPathRef类型,通过矢量图形而不是bitmap来绘制的图层子类
  • 渲染快速。CAShapeLayer使用了硬件加速,绘制同一图形会比用Core Graphics快很多。
  • 高效使用内存。一个CAShapeLayer不需要像普通CALayer一样创建一个寄宿图形,所以无论有多大,都不会占用太多的内存。
  • 不会被图层边界剪裁掉。一个CAShapeLayer可以在边界之外绘制。你的图层路径不会像在使用Core Graphics的普通CALayer一样被剪裁掉。
  • 不会出现像素化。当你给CAShapeLayer做3D变换时,它不像一个有寄宿图的普通图层一样变得像素化。
// 简单使用
  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)];

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;

[self.containerView.layer addSublayer:shapeLayer];


Snip20170527_1.png
圆角(可以单独指定每个角)
  • 绘制一个有三个圆角一个直角的矩形
CGRect rect = CGRectMake(50, 50, 100, 100);

CGSize radii = CGSizeMake(20, 20);
UIRectCorner corners = UIRectCornerTopRight | UIRectCornerBottomRight | UIRectCornerBottomLeft;
//create path
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:corners cornerRadii:radii];

* 把`CAShapeLayer`作为视图的宿主图层,而不是添加一个子视图(**图层蒙板的`mask`属性**)

####<a name="markdown-two"></a>CATextLayer
* 使用了`Core text`来实现绘制的,比`UILabel`渲染得快得多.

```swift
// 简单使用
  CATextLayer *textLayer = [CATextLayer layer];

textLayer.frame = self.labelView.bounds;
[self.labelView.layer addSublayer:textLayer];

textLayer.foregroundColor = [UIColor blackColor].CGColor;
textLayer.alignmentMode = kCAAlignmentJustified;
textLayer.wrapped = YES;

UIFont *font = [UIFont systemFontOfSize:15];

CFStringRef fontName = (__bridge CFStringRef)font.fontName;
CGFontRef fontRef = CGFontCreateWithFontName(fontName);
textLayer.font = fontRef;
textLayer.fontSize = font.pointSize;
CGFontRelease(fontRef);

textLayer.contentsScale = [UIScreen mainScreen].scale;
NSString *text = @"Lorem ipsum dolor sit amet, consectetur adipiscing \ elit. Quisque massa arcu, eleifend vel varius in, facilisis pulvinar \ leo. Nunc quis nunc at mauris pharetra condimentum ut ac neque. Nunc elementum, libero ut porttitor dictum, diam odio congue lacus, vel \ fringilla sapien diam at purus. Etiam suscipit pretium nunc sit amet \ lobortis";

textLayer.string = text;

* `CATextLayer`的`font`属性是一个`CFTypeRef`类型,可以根据你的具体需要来决定字体属性应该是用`CGFontRef`类型还是`CTFontRef`类型(`Core Text`字体)。
* `CATextLayer`的`string`属性是 `id` 类型,这样既可以用`NSString`也可以用`NSAttributedString`来指定文本了。


Snip20170531_1.png
富文本
  • NSTextAttributeName针对iOS 6及以上,此处演示是在iOS 5及以下
  CATextLayer *textLayer = [CATextLayer layer];

textLayer.frame = self.labelView.bounds;
textLayer.contentsScale = [UIScreen mainScreen].scale;
[self.labelView.layer addSublayer:textLayer];

textLayer.alignmentMode = kCAAlignmentJustified;
textLayer.wrapped = YES;

UIFont *font = [UIFont systemFontOfSize:15];

NSString * text = @"Lorem ipsum dolor sit amet, consectetur adipiscing \ elit. Quisque massa arcu, eleifend vel varius in, facilisis pulvinar \ leo. Nunc quis nunc at mauris pharetra condimentum ut ac neque. Nunc \ elementum, libero ut porttitor dictum, diam odio congue lacus, vel \ fringilla sapien diam at purus. Etiam suscipit pretium nunc sit amet \ lobortis";

NSMutableAttributedString *string = nil;
string = [[NSMutableAttributedString alloc] initWithString:text];

CFStringRef fontName = (__bridge CFStringRef)font.fontName;
CGFloat fontSize = font.pointSize;
CTFontRef fontRef = CTFontCreateWithName(fontName, fontSize, NULL);

NSDictionary *attribs = @{

 (__bridge id)kCTForegroundColorAttributeName:(__bridge id)[UIColor blackColor].CGColor,
 (__bridge id)kCTFontAttributeName: (__bridge id)fontRef

};

[string setAttributes:attribs range:NSMakeRange(0, [text length])];
attribs = @{
(__bridge id)kCTForegroundColorAttributeName: (__bridge id)[UIColor redColor].CGColor,
(__bridge id)kCTUnderlineStyleAttributeName: @(kCTUnderlineStyleSingle),
(__bridge id)kCTFontAttributeName: (__bridge id)fontRef
};
[string setAttributes:attribs range:NSMakeRange(6, 5)];

CFRelease(fontRef);

textLayer.string = string;



Snip20170531_2.png
行距和字距
  • CATextLayer渲染和用UILabel渲染出的文本行距和字距不相同的。
UILabel的替代品
  • 以下演示了一个UILabel子类LayerLabelCATextLayer绘制它的问题,不是调用一般的UILabel使用的较慢的-drawRect:方法。LayerLabel示例既可以用代码实现,也可以在Interface Builder中实现,只要把普通的标签拖入视图之中,然后设置它的类是LayerLabel就可以了。
#import "LayerLabel.h"

import

@implementation LayerLabel

  • (Class)layerClass
    {
    return [CATextLayer class];
    }
  • (CATextLayer *)textLayer
    {
    return (CATextLayer *)self.layer;
    }
  • (void)setUp
    {
    self.text = self.text;
    self.textColor = self.textColor;
    self.font = self.font;

[self textLayer].alignmentMode = kCAAlignmentJustified;

[self textLayer].wrapped = YES;
[self.layer display];
}

  • (id)initWithFrame:(CGRect)frame
    {
    if (self = [super initWithFrame:frame]) {
    [self setUp];
    }
    return self;
    }
  • (void)awakeFromNib
    {
    [self setUp];
    }
  • (void)setText:(NSString *)text
    {
    super.text = text;
    [self textLayer].string = text;
    }
  • (void)setTextColor:(UIColor *)textColor
    {
    super.textColor = textColor;
    [self textLayer].foregroundColor = textColor.CGColor;
    }
  • (void)setFont:(UIFont *)font
    {
    super.font = font;
    CFStringRef fontName = (__bridge CFStringRef)font.fontName;
    CGFontRef fontRef = CGFontCreateWithFontName(fontName);
    [self textLayer].font = fontRef;
    [self textLayer].fontSize = font.pointSize;

    CGFontRelease(fontRef);
    }
    @end
* 把`CATextLayer`作为宿主图层的另一好处就是视图自动设置了`contentsScale`属性。
* 如果你打算支持`iOS 6`及以上,基于`CATextLayer`的标签可能就有有些局限性。但是总得来说,如果想在app里面充分利用`CALayer`子类,用`+layerClass`来创建基于不同图层的视图是一个简单可复用的方法。

####<a name="markdown-three"></a>CATransformLayer
* `CATransformLayer`不能显示它自己的内容。只有当存在了一个能作用域子图层的变换它才真正存在。`CATransformLayer`并不平面化它的子图层,所以它能够用于构造一个层级的3D结构。

* 同样的代码,用`CALayer`

![22.gif](http://upload-images.jianshu.io/upload_images/2390274-40447b8b4c474fd3.gif?imageMogr2/auto-orient/strip)

* 同样的代码,用`CATransformLayer`


11.gif
  • 简单使用
// 普通的一个layer
CALayer *plane        = [CALayer layer];
plane.anchorPoint = CGPointMake(0.5, 0.5);                         // 锚点
plane.frame       = (CGRect){CGPointZero, CGSizeMake(100, 100)};   // 尺寸
plane.position    = CGPointMake(200, V_CENTER_Y);                  // 位置
plane.opacity         = 0.6;                                       // 背景透明度
plane.backgroundColor = CG_COLOR(0, 1, 0, 1);                      // 背景色
plane.borderWidth     = 3;                                         // 边框宽度
plane.borderColor     = CG_COLOR(1, 1, 1, 0.5);                    // 边框颜色(设置了透明度)
plane.cornerRadius    = 10;                                        // 圆角值
// Z轴平移
CATransform3D plane_3D = CATransform3DIdentity;
plane_3D               = CATransform3DTranslate(plane_3D, 0, 0, -30);
plane.transform        = plane_3D;
// 创建容器layer
CATransformLayer *container = [CATransformLayer layer];
container.frame    = self.view.bounds;
[self.view.layer addSublayer:container];
[container addSublayer:plane];
// 启动定时器
_timer = [[GCDTimer alloc] initInQueue:[GCDQueue mainQueue]];
[_timer event:^{
    static float degree = 0.f;
    // 起始值
    CATransform3D fromValue = CATransform3DIdentity;
    fromValue.m34           = 1.0/ -500;
    fromValue               = CATransform3DRotate(fromValue, degree, 0, 1, 0);
    // 结束值
    CATransform3D toValue   = CATransform3DIdentity;
    toValue.m34             = 1.0/ -500;
    toValue                 = CATransform3DRotate(toValue, degree += 45.f, 0, 1, 0);
    // 添加3d动画
    CABasicAnimation *transform3D = [CABasicAnimation animationWithKeyPath:@"transform"];
    transform3D.duration  = 1.f;
    transform3D.fromValue = [NSValue valueWithCATransform3D:fromValue];
    transform3D.toValue   = [NSValue valueWithCATransform3D:toValue];
    container.transform = toValue;
    [container addAnimation:transform3D forKey:@"transform3D"];

} timeInterval:NSEC_PER_SEC];
[_timer start];

* 通过如下两张图片能够更好的体现出它的作用(上面用 `CALayer`,下面用`CAGradientLayer`)。


Snip20170601_1.png
//给planes应用变换

t = CATransform3DIdentity;
t = CATransform3DTranslate(t, 0, 0, -10);
purplePlane.transform = t;

t = CATransform3DIdentity;
t = CATransform3DTranslate(t, 0, 0, -50);
redPlane.transform = t;

t = CATransform3DIdentity;
t = CATransform3DTranslate(t, 0, 0, -90);
orangePlane.transform = t;

t = CATransform3DIdentity;
t = CATransform3DTranslate(t, 0, 0, -130);
yellowPlane.transform = t;

* 同样的代码,可见`CALayer`不能够管理3D层级的深度。`CATransformLayer`并不平面化它的子图层,能够用于构造一个层级的3D结构,

####<a name="markdown-four"></a>CAGradientLayer
* `CAGradientLayer`是用来生成两种或更多颜色平滑渐变的,真正好处在于绘制使用了硬件加速。

#####基础渐变
* `CAGradientLayer`有`startPoint`和`endPoint`属性,决定了渐变的方向。这两个参数是以单位坐标系进行的定义,所以左上角坐标是{0, 0},右下角坐标是{1, 1}。

```swift
  CAGradientLayer *gradientLayer = [CAGradientLayer layer];

gradientLayer.frame = self.containerView.bounds;
[self.containerView.layer addSublayer:gradientLayer];

//gradientLayer.colors = @[(id)[UIColor redColor].CGColor,(id)[UIColor blueColor].CGColor];
gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor, (__bridge id)[UIColor blueColor].CGColor];

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


Snip20170531_3.png
多重渐变
  • colors属性可以包含很多颜色,在空间上均匀地被渲染。可以用locations属性来调整空间。locations属性是一个浮点数值的数组(以NSNumber包装)。定义了colors属性中每个不同颜色的位置,同样的,也是以单位坐标系进行标定。0.0代表着渐变的开始,1.0代表着结束.
  • locations数组并不是强制要求的,但是如果你给它赋值了就一定要确保locations的数组大小和colors数组大小一定要相同,否则你将会得到一个空白的渐变。
  CAGradientLayer *gradientLayer = [CAGradientLayer layer];

gradientLayer.frame = self.containerView.bounds;
[self.containerView.layer addSublayer:gradientLayer];

gradientLayer.colors = @[(__bridge id)[UIColor redColor].CGColor, (__bridge id) [UIColor yellowColor].CGColor, (__bridge id)[UIColor greenColor].CGColor];

gradientLayer.locations = @[@0.0, @0.25, @0.5];

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


Snip20170531_4.png

<a name="markdown-five"></a>CAReplicatorLayer

  • 目的是为了高效生成许多相似的图层。
重复图层
  • instanceCount属性指定了图层需要重复多少次。
  • instanceTransform指定了一个CATransform3D3D变换。变换是逐步增加的,每个实例都是相对于前一实例布局。这就是为什么这些复制体最终不会出现在同意位置上.
    CAReplicatorLayer *replicator = [CAReplicatorLayer layer];
replicator.frame = self.containerView.bounds;
[self.containerView.layer addSublayer:replicator];
replicator.instanceCount = 10;
CATransform3D transform = CATransform3DIdentity;
transform = CATransform3DTranslate(transform, 0, 200, 0);
transform = CATransform3DRotate(transform, M_PI / 5.0, 0, 0, 1);
transform = CATransform3DTranslate(transform, 0, -200, 0);
replicator.instanceTransform = transform;
replicator.instanceBlueOffset = -0.1;
replicator.instanceGreenOffset = -0.1;
CALayer *layer = [CALayer layer];
layer.frame = CGRectMake(100.0f, 100.0f, 100.0f, 100.0f);
layer.backgroundColor = [UIColor whiteColor].CGColor;
[replicator addSublayer:layer];

Snip20170601_5.png
  • instanceBlueOffsetinstanceGreenOffset属性通过逐步减少蓝色和绿色通道,逐渐将图层颜色转换成了红色。
反射
  • 使用CAReplicatorLayer并应用一个负比例变换于一个复制图层,你就可以创建指定视图内容的镜像图片,这样就创建了一个实时的『反射』效果。
#import "ReflectionView.h"

import

@implementation ReflectionView

  • (Class)layerClass
    {
    return [CAReplicatorLayer class];
    }
  • (void)setUp
    {
    CAReplicatorLayer *layer = (CAReplicatorLayer *)self.layer;
    layer.instanceCount = 2;
CATransform3D transform = CATransform3DIdentity;
CGFloat verticalOffset = self.bounds.size.height + 2;
transform = CATransform3DTranslate(transform, 0, verticalOffset, 0);
transform = CATransform3DScale(transform, 1, -1, 0);
layer.instanceTransform = transform;
layer.instanceAlphaOffset = -0.6;

}

  • (id)initWithFrame:(CGRect)frame
    {
    if ((self = [super initWithFrame:frame])) {
    [self setUp];
    }
    return self;
    }
  • (void)awakeFromNib
    {
    [self setUp];
    }
    @end

Snip20170601_2.png

<a name="markdown-six"></a>CAScrollLayer

  • 作用相当于UIScrollView,但scrollerView是控件与控件之间的滑动,这是图层与图层之间的滑动。
  • CAScrollLayer的可滚动区域的范围是由它的子层布局来确定的。CAScrollLayer不提供键盘或鼠标事件处理,也没有提供可见滚动条。
#import "ScrollLayer.h"

@interface ScrollLayer ()
@property (nonatomic, strong) CAScrollLayer *scrollLayer;
@end
@implementation ScrollLayer

  • (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
CALayer *layer = [CALayer layer];
layer.contents = (id)[UIImage imageNamed:@"bg.jpg"].CGImage;
layer.frame = CGRectMake(80, 80, 100, 100);
self.scrollLayer = [CAScrollLayer layer];
self.scrollLayer.frame = CGRectMake(60, 80, 200, 200);
self.scrollLayer.backgroundColor = [UIColor orangeColor].CGColor;
[self.scrollLayer addSublayer:layer];
self.scrollLayer.scrollMode = kCAScrollBoth;
[self.view.layer addSublayer:self.scrollLayer];
// 这个判断只是判断手势,可以先判断出发点是否在当前的大小里再去响应手势
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:pan];

}

  • (void)pan:(UIPanGestureRecognizer *)recognizer {
    CGPoint offset = self.scrollLayer.bounds.origin;
    offset.x -= [recognizer translationInView:self.view].x;
    offset.y -= [recognizer translationInView:self.view].y;
[self.scrollLayer scrollToPoint:offset];
[recognizer setTranslation:CGPointZero inView:self.view];

}


333.gif
  • scrollMode:设置滚动的方向。
  • scrollToPoint:自动适应bounds的原点以便图层内容出现在滑动的地方。
  • scrollToRect:滚动图层的内容以确保该区域可见。
  • 扩展分类
  • scrollPoint:方法是从自身开始往父图层找到最近的CAScrollLayer层,然后调用scrollToPoint:方法,如果没有找到CAScrollLayer层则不做任何处理。
  • scrollRectToVisible:方法是从自身开始往父图层找到最近的CAScrollLayer层,然后调用scrollToRect:方法,如果没有找到CAScrollLayer层则不做任何处理。
  • visibleRect返回可见区域范围。

<a name="markdown-seven"></a>CATiledLayer

  • 所有显示在屏幕上的图片最终都会被转化为OpenGL纹理,同时OpenGL有一个最大的纹理尺寸(通常是2048*2048,或4096*4096,这个取决于设备型号)。
  • 如果你想在单个纹理中显示一个比这大的图,即便图片已经存在于内存中了,你仍然会遇到很大的性能问题,因为Core Animation强制用CPU处理图片而不是更快的GPU
  • CATiledLayer为载入大图造成的性能问题提供了一个解决方案:将大图分解成小片然后将他们单独按需载入。
  • CATiledLayer优势的基础是先把这个图片裁切成许多小一些的图片。如果在运行时读入整个图片并裁切,那CATiledLayer的性能优点就损失殆尽了。
小片裁剪
  • 将一张大的图片裁切成许多小一些的图片。(Mac程序)
#import 

int main(int argc, const char * argv[])
{
@autoreleasepool{
if (argc < 2) {
NSLog(@"TileCutter arguments: inputfile");
return 0;
}
NSString *inputFile = [NSString stringWithCString:argv[1] encoding:NSUTF8StringEncoding];

    CGFloat tileSize = 256; //output path
    NSString *outputPath = [inputFile stringByDeletingPathExtension];
    NSImage *image = [[NSImage alloc] initWithContentsOfFile:inputFile];
    NSSize size = [image size];
    NSArray *representations = [image representations];
    if ([representations count]){
        NSBitmapImageRep *representation = representations[0];
        size.width = [representation pixelsWide];
        size.height = [representation pixelsHigh];
    }
    NSRect rect = NSMakeRect(0.0, 0.0, size.width, size.height);
    CGImageRef imageRef = [image CGImageForProposedRect:&rect context:NULL hints:nil];
    NSInteger rows = ceil(size.height / tileSize);
    NSInteger cols = ceil(size.width / tileSize);
   for (int y = 0; y < rows; ++y) {
        for (int x = 0; x < cols; ++x) {
        CGRect tileRect = CGRectMake(x*tileSize, y*tileSize, tileSize, tileSize);
        CGImageRef tileImage = CGImageCreateWithImageInRect(imageRef, tileRect);
        NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:tileImage];
        NSData *data = [imageRep representationUsingType: NSJPEGFileType properties:nil];
        CGImageRelease(tileImage);
       NSString *path = [outputPath stringByAppendingFormat: @"_%02i_%02i.jpg", x, y];
        [data writeToFile:path atomically:NO];
        }
    }
}
return 0;

}

* `256*256`是`CATiledLayer`的默认小图大小,可以通过`tileSize`属性更改。`tileSize`是以像素为单位,而不是点。
* 运行结果是64个新图的序列

```swift
#import "ViewController.h"

import

@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIScrollView *scrollView;
@end
@implementation ViewController

  • (void)viewDidLoad
    {
    [super viewDidLoad];
    CATiledLayer *tileLayer = [CATiledLayer layer];
    tileLayer.frame = CGRectMake(0, 0, 2048, 2048);
    tileLayer.delegate = self;
    [self.scrollView.layer addSublayer:tileLayer];
self.scrollView.contentSize = tileLayer.frame.size;
[tileLayer setNeedsDisplay];

}

  • (void)drawLayer:(CATiledLayer *)layer inContext:(CGContextRef)ctx
    {
    // 获取裁剪区域
    CGRect bounds = CGContextGetClipBoundingBox(ctx);
    // 确定坐标
    NSInteger x = floor(bounds.origin.x / layer.tileSize.width);
    NSInteger y = floor(bounds.origin.y / layer.tileSize.height);
NSString *imageName = [NSString stringWithFormat: @"Snowman_%02i_%02i", x, y];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"jpg"];
UIImage *tileImage = [UIImage imageWithContentsOfFile:imagePath];

//使用UIKit进行绘制,因为UIKit只会对当前上下文栈顶的context操作,所以要把形参中的context设置为当前上下文

UIGraphicsPushContext(ctx);

//指定位置和大小绘制图片
[tileImage drawInRect:bounds];
UIGraphicsPopContext();
}
@end

* 实现`-drawLayer:inContext:`方法,当需要载入新的小图时,`CATiledLayer`就会调用到这个方法。
* **`UIGraphicsPushContext`和`UIGraphicsPopContext`的作用?**
 * `UIGraphicsPushContext`并不能保存上下文的当前**状态**(画笔颜色、线条宽度等),而是完全切换上下文。
 * 假设你正在当前视图上下文中绘制什么东西,这时想要在位图上下文中绘制完全不同的东西。如果要使用`UIKit`来进行任意绘图,你会希望保存当前的`UIKit`上下文,包括所有已经绘制的内容,接着切换到一个全新的绘图上下文中。这就是`UIGraphicsPushContext`的功能。创建完位图后,再将你的旧上下文出栈。而这就是`UIGraphicsPopContext`的功能。


Snip20170610_1.png
  • CATiledLayer载入小图的时候,会淡入效果,可以用fadeDuration属性改变淡入时长或直接禁用掉。
  • -drawLayer:inContext:方法可以在多个线程中同时地并发调用,所以请小心谨慎地确保你在这个方法中实现的绘制代码是线程安全的。

<a name="markdown-eight"></a>CAEmitterLayer

  • CAEmitterLayer是一个高性能的粒子引擎,被用来创建实时例子动画如:烟雾,火,雨等等这些效果。
  • 简单使用
  CAEmitterLayer *snowEmitter = [CAEmitterLayer layer];

//例子发射位置
snowEmitter.emitterPosition = CGPointMake(120,20);
//发射源的尺寸大小
snowEmitter.emitterSize = CGSizeMake(self.view.bounds.size.width * 20, 20);
//发射模式
snowEmitter.emitterMode = kCAEmitterLayerSurface;
//发射源的形状
snowEmitter.emitterShape = kCAEmitterLayerLine;

//创建雪花类型的粒子
CAEmitterCell *snowflake = [CAEmitterCell emitterCell];
//粒子的名字
snowflake.name = @"snow";
//粒子参数的速度乘数因子
snowflake.birthRate = 1.0;
snowflake.lifetime = 120.0;

//粒子速度
snowflake.velocity =10.0;
//粒子的速度范围
snowflake.velocityRange = 10;
//粒子y方向的加速度分量
snowflake.yAcceleration = 2;
//周围发射角度
snowflake.emissionRange = 0.5 * M_PI;
//子旋转角度范围
snowflake.spinRange = 0.25 * M_PI;
snowflake.contents = (id)[[UIImage imageNamed:@"DazFlake"] CGImage];
//设置雪花形状的粒子的颜色
snowflake.color = [[UIColor colorWithRed:0.200 green:0.258 blue:0.543 alpha:1.000] CGColor];

//创建星星形状的粒子
CAEmitterCell *snowflake1 = [CAEmitterCell emitterCell];
//粒子的名字
snowflake1.name = @"snow";
//粒子参数的速度乘数因子

snowflake1.birthRate = 1.0;
snowflake1.lifetime = 120.0;
//粒子速度
snowflake1.velocity =10.0;
//粒子的速度范围
snowflake1.velocityRange = 10;
//粒子y方向的加速度分量
snowflake1.yAcceleration = 2;
//周围发射角度
snowflake1.emissionRange = 0.5 * M_PI;
//子旋转角度范围
snowflake1.spinRange = 0.25 * M_PI;
//粒子的内容和内容的颜色
snowflake1.contents = (id)[[UIImage imageNamed:@"DazStarOutline"] CGImage];
snowflake1.color = [[UIColor colorWithRed:0.600 green:0.658 blue:0.743 alpha:1.000] CGColor];

snowEmitter.shadowOpacity = 1.0;
snowEmitter.shadowRadius = 0.0;
snowEmitter.shadowOffset = CGSizeMake(0.0, 1.0);
//粒子边缘的颜色
snowEmitter.shadowColor = [[UIColor redColor] CGColor];

snowEmitter.emitterCells = [NSArray arrayWithObjects:snowflake,snowflake1,nil];
[self.view.layer insertSublayer:snowEmitter atIndex:0];

* `CAEMitterCell`属性介绍
 * `alphaRange:` 一个粒子的颜色`alpha`能改变的范围;
 * `alphaSpeed:`粒子透明度在生命周期内的改变速度;
 * `birthrate:`粒子参数的速度乘数因子;
 * `blueRange:`一个粒子的颜色能改变的范围;
 * `blueSpeed: `粒子在生命周期内的改变速度;
 * `color:`粒子的颜色
 * `contents:`是个`CGImageRef`的对象,既粒子要展现的图片;
 * `contentsRect:`应该画在`contents`里的子`rectangle:`
 * `emissionLatitude:`发射的z轴方向的角度
 * `emissionLongitude:`x-y平面的发射方向
 * `emissionRange;`周围发射角度 
 * `emitterCells:`粒子发射的粒子
 * `enabled:`粒子是否被渲染
 * `greenrange: `一个粒子的颜色green 能改变的范围;
 * `greenSpeed: `粒子green在生命周期内的改变速度;
 * `lifetime:`生命周期
 * `lifetimeRange:`生命周期范围
 * `magnificationFilter:`不是很清楚好像增加自己的大小
 * `minificatonFilter:`减小自己的大小
 * `minificationFilterBias:`减小大小的因子
 * `name:`粒子的名字
 * `redRange:`一个粒子的颜色red 能改变的范围;
 * `redSpeed;` 粒子red在生命周期内的改变速度;
 * `scale:`缩放比例:
 * `scaleRange:`缩放比例范围;
 * `scaleSpeed:`缩放比例速度:
 * `spin:`子旋转角度
 * `spinrange:`子旋转角度范围
 * `style:`不是很清楚:
 * `velocity:`速度
 * `velocityRange:`速度范围
 * `xAcceleration:`粒子x方向的加速度分量
 * `yAcceleration:`粒子y方向的加速度分量
 * `zAcceleration:`粒子z方向的加速度分量
* `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:`不是多很清楚(**是否将3D例子系统平面化到一个图层(默认值)或者可以在3D空间中混合其他的图层**)
> * `renderMode:`渲染模式:(**控制着在视觉上粒子图片是如何混合的。应该是指重叠部分。**)
>   * `NSString * const kCAEmitterLayerUnordered;`默认
>   * `NSString * const kCAEmitterLayerOldestFirst;`
>   * `NSString * const kCAEmitterLayerOldestLast;`
>   * `NSString * const kCAEmitterLayerBackToFront;`
>   * `NSString * const kCAEmitterLayerAdditive;`
> * `scale:`粒子的缩放比例:
> * `seed:`用于初始化随机数产生的种子
> * `spin:`自旋转速度
> * `velocity:`粒子速度

####<a name="markdown-night"></a>(CAEAGLLayer/CAOpenGLLayer)
* 处理高性能图形绘制
* `CAEAGLLayer`提供了一个`OpenGLES`渲染环境。各种各样的`OpenGL`绘图缓冲的底层可配置项仍然需要你用`CAEAGLLayer`完成,它是`CALayer`的一个子类,用来显示任意的`OpenGL`图形。`OpenGL`由近350个不同的函数调用组成,用来从简单的图元绘制复杂的三维景象,主要用途是`CAD`、科学可视化程序、虚拟现实、游戏程序设计。

####<a name="markdown-ten"></a>CAMetalLayer(需要连上真机,才会出现CAMetalLayer文件,至少5S)
* 是核心动画层使用`Metal`管理的一个`Layer`,
* `Metal`和`OpenGL ES`相似,它也是一个底层`API`,负责和`3D`绘图硬件交互。它们之间的不同在于,`Metal`不是跨平台的。与之相反的,它设计的在苹果硬件上运行得极其高效,与`OpenGL ES`相比,它提供了更快的速度和更低的开销。
* 创建新项目时,选择游戏开发,如下图:


Snip20170612_5.png

<a name="markdown-onee"></a>AVPlayerLayer

  • 不是Core Animation框架的一部分,由AVFoundation提供。
  • 是高级接口例如MPMoivePlayer的底层实现,提供了显示视频的底层控制。
#import "ViewController.h"

import AVFoundation

import

@interface ViewController ()
@property (nonatomic, weak) IBOutlet UIView *containerView;
@end
@implementation ViewController

  • (void)viewDidLoad
    {
    [super viewDidLoad];
    NSURL *URL = [[NSBundle mainBundle] URLForResource:@"Ship" withExtension:@"mp4"];
AVPlayer *player = [AVPlayer playerWithURL:URL];
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];
playerLayer.frame = self.containerView.bounds;
[self.containerView.layer addSublayer:playerLayer];
[player play];

}
@end

* 把它添加到了一个容器视图中,而不是直接在`controller`中的主视图上添加。这样是为了可以使用自动布局限制使得图层在最中间;否则,一旦设备被旋转了我们就要手动重新放置位置,`Core Animation`并不支持自动大小和自动布局。

* 因为`AVPlayerLayer`是`CALayer`的子类,它继承了父类的所有特性。我们并不会受限于要在一个矩形中播放视频.

```swift
- (void)viewDidLoad

{
playerLayer.frame = self.containerView.bounds;
[self.containerView.layer addSublayer:playerLayer];

CATransform3D transform = CATransform3DIdentity;
transform.m34 = -1.0 / 500.0;
transform = CATransform3DRotate(transform, M_PI_4, 1, 1, 0);
playerLayer.transform = transform;
playerLayer.masksToBounds = YES;
playerLayer.cornerRadius = 20.0;
playerLayer.borderColor = [UIColor redColor].CGColor;
playerLayer.borderWidth = 5.0;
[player play];

}


####<a name="markdown-twoo"></a>demo
[[简单demo]LiDechao的demo](https://github.com/LiDechao/LayerAnimation)

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

推荐阅读更多精彩内容