《iOS核心動畫高階技巧》筆記(四) - 專用圖層

weixin_34234823發表於2017-06-12

專用圖層

簡書很多標籤不支援,以下目錄跳轉沒用!!!

<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];


2390274-3d22143ff4fb2d3a.png
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`來指定文字了。


2390274-9dea523e926bc9ad.png
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;



2390274-10efe140822519fe.png
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`


2390274-bb3fd213df1c3c1a.gif
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`)。


2390274-9683f6a75034cdd4.png
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);


2390274-6088b02ae1aceed9.png
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);


2390274-6ec1d8194014db1c.png
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];

2390274-9a578338a5a3de8e.png
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

2390274-418139f13c1912ef.png
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];

}


2390274-6b2784d6d40954cb.gif
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`的功能。


2390274-7a2253f9185b0e4e.png
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`相比,它提供了更快的速度和更低的開銷。
* 建立新專案時,選擇遊戲開發,如下圖:


2390274-f9675dc67513707b.png
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)

相關文章