专用图层,CoreAnimation之专用图层

CAScrollLayer

对此三个未改动的图层,它的bounds和它的frame是千篇壹律的,frame品质是由bounds属性自动测算而出的,所以改动任意2个值都会更新任何值。

唯独1旦您只想体现一个大图层里面包车型地铁一小部分吗。比如说,你恐怕有多少个十分的大的图样,你期望用户能够自由滑动,或许是3个多少或文本的长列表。在3个超级的iOS应用中,你大概会用到UITableView或是UIScrollView,不过对于单身的图层来说,什么会等价于刚先生刚提到的UITableViewUIScrollView呢?

在第壹章中,我们研商了图层的contentsRect品质的用法,它真的是能够化解在图层中型小型地点显得大图片的化解办法。可是借使您的图层包罗子图层那它就不是三个更好的缓解方案,因为,那样做的话每一遍你想『滑动』可视区域的时候,你就必要手工重新总括并立异拥有的子图层地方。

那年就须要CAScrollLayer了。CAScrollLayer有一个-scrollToPoint:艺术,它自动适应bounds的原点以便图层内容出现在滑行的地点。注意,那便是它做的全体事情。前边提到过,Core
Animation并不处理用户输入,所以CAScrollLayer并不担负将触摸事件转换为滑行事件,既不渲染滚动条,也不兑现别的iOS钦定行为例如滑动反弹(当视图滑动超多了它的境界的将会反弹回正确的地点)。

让咱们来用CAScrollLayer来科学普及3个主旨的UIScrollView代替品。我们将会用CAScrollLayer用作视图的宿主图层,并创办三个自定义的UIView,然后用UIPanGestureRecognizer兑现触摸事件响应。那段代码见清单六.十.
图六.11是运作效果:ScrollView呈现了一个当先它的frameUIImageView

清单6.10 用CAScrollLayer兑现滑动视图

 

澳门葡京备用网址 1澳门葡京备用网址 2

 1 #import "ScrollView.h"
 2 #import  @implementation ScrollView
 3 + (Class)layerClass
 4 {
 5     return [CAScrollLayer class];
 6 }
 7 
 8 - (void)setUp
 9 {
10     //enable clipping
11     self.layer.masksToBounds = YES;
12 
13     //attach pan gesture recognizer
14     UIPanGestureRecognizer *recognizer = nil;
15     recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
16     [self addGestureRecognizer:recognizer];
17 }
18 
19 - (id)initWithFrame:(CGRect)frame
20 {
21     //this is called when view is created in code
22     if ((self = [super initWithFrame:frame])) {
23         [self setUp];
24     }
25     return self;
26 }
27 
28 - (void)awakeFromNib {
29     //this is called when view is created from a nib
30     [self setUp];
31 }
32 
33 - (void)pan:(UIPanGestureRecognizer *)recognizer
34 {
35     //get the offset by subtracting the pan gesture
36     //translation from the current bounds origin
37     CGPoint offset = self.bounds.origin;
38     offset.x -= [recognizer translationInView:self].x;
39     offset.y -= [recognizer translationInView:self].y;
40 
41     //scroll the layer
42     [(CAScrollLayer *)self.layer scrollToPoint:offset];
43 
44     //reset the pan gesture translation
45     [recognizer setTranslation:CGPointZero inView:self];
46 }
47 @end

View Code

图6.11 用UIScrollView创建八个汇合的滑行视图

不同于UIScrollView,大家定制的滑行视图类并从未落实任何款式的界线检查(bounds
checking)。图层内容极有一点都不小希望滑出视图的分界并Infiniti滑下去。CAScrollLayer并未一样UIScrollViewcontentSize的属性,所以当CAScrollLayer滑动的时候完全未有一个大局的可滑动区域的定义,也手足无措自适应它的界限原点至你钦定的值。它之所以不能够自适应边界大小是因为它不必要,内容完全能够超越界限。

那你一定会意外用CAScrollLayer的意思毕竟何在,因为您能够轻巧地用1个惯常的CALayer然后手动适应边界原点啊。真相其实并不复杂,UIScrollView并从未用CAScrollLayer,事实上,正是轻易的通过直接操作图层边界来完毕滑动。

CAScrollLayer有贰个隐私的有用特色。假使您查看CAScrollLayer的头文件,你就会专注到有一个扩大分类完成了有些措施和属性:

1 - (void)scrollPoint:(CGPoint)p;
2 - (void)scrollRectToVisible:(CGRect)r;
3 @property(readonly) CGRect visibleRect;

 

总的来看这个办法和属性名,你恐怕会认为那几个点子给种种CALayer实例增添了滑动功效。然则其实他们只是放置在CAScrollLayer中的图层的实用方法。scrollPoint:措施从图层树中探求并找到第一个可用的CAScrollLayer,然后滑动它使得钦定点形成可视的。scrollRectToVisible:主意达成了扳平的业务只不过是效益在多个矩形上的。visibleRect属性决定图层(假设存在的话)的哪部分是如今的可视区域。要是您本人完成那几个办法就会相对轻易明白有些,不过CAScrollLayer帮您省了那些麻烦,所以当提到到落到实处图层滑动的时候就能够用上了。

 

CAScrollLayer

对于1个未改换的图层,它的bounds和它的frame是同等的,frame质量是由bounds属性自动总结而出的,所以退换任意多个值都会更新任何值。

唯独一旦你只想呈现一个大图层里面包车型大巴一小部分吗。比如说,你恐怕有3个十分的大的图样,你指望用户能够随意滑动,恐怕是贰个数目或文本的长列表。在一个数壹数2的iOS应用中,你恐怕会用到UITableView或是UIScrollView,可是对于单身的图层来讲,什么会等价于刚同志刚提到的UITableViewUIScrollView呢?

在其次章中,大家探索了图层的contentsRect属性的用法,它实在是能够缓解在图层中型小型地点显得大图片的消除办法。但是假如你的图层包涵子图层那它就不是贰个百般好的缓解方案,因为,那样做的话每一次你想『滑动』可视区域的时候,你就要求手工业重新总括并革新具备的子图层地方。

今年就须求CAScrollLayer了。CAScrollLayer有一个-scrollToPoint:方法,它自动适应bounds的原点以便图层内容出现在滑行的地点。注意,那便是它做的兼具事情。前边提到过,Core
Animation并不处理用户输入,所以CAScrollLayer并不担负将触摸事件调换为滑行事件,既不渲染滚动条,也不落到实处其余iOS钦命行为例如滑动反弹(当视图滑动超多了它的疆界的将会反弹回正确的地点)。

让大家来用CAScrollLayer来广大学一年级个大旨的UIScrollView替代品。大家将会用CAScrollLayer用作视图的宿主图层,并创设多个自定义的UIView,然后用UIPanGestureRecognizer福寿年高触摸事件响应。那段代码见清单陆.十.
图六.1壹是运作效果:ScrollView来得了三个超乎它的frameUIImageView

清单6.10 用CAScrollLayer福寿双全滑动视图

 

澳门葡京备用网址 3澳门葡京备用网址 4

 1 #import "ScrollView.h"
 2 #import  @implementation ScrollView
 3 + (Class)layerClass
 4 {
 5     return [CAScrollLayer class];
 6 }
 7 
 8 - (void)setUp
 9 {
10     //enable clipping
11     self.layer.masksToBounds = YES;
12 
13     //attach pan gesture recognizer
14     UIPanGestureRecognizer *recognizer = nil;
15     recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
16     [self addGestureRecognizer:recognizer];
17 }
18 
19 - (id)initWithFrame:(CGRect)frame
20 {
21     //this is called when view is created in code
22     if ((self = [super initWithFrame:frame])) {
23         [self setUp];
24     }
25     return self;
26 }
27 
28 - (void)awakeFromNib {
29     //this is called when view is created from a nib
30     [self setUp];
31 }
32 
33 - (void)pan:(UIPanGestureRecognizer *)recognizer
34 {
35     //get the offset by subtracting the pan gesture
36     //translation from the current bounds origin
37     CGPoint offset = self.bounds.origin;
38     offset.x -= [recognizer translationInView:self].x;
39     offset.y -= [recognizer translationInView:self].y;
40 
41     //scroll the layer
42     [(CAScrollLayer *)self.layer scrollToPoint:offset];
43 
44     //reset the pan gesture translation
45     [recognizer setTranslation:CGPointZero inView:self];
46 }
47 @end

View Code

图6.11 用UIScrollView成立叁个凑合的滑行视图

不同于UIScrollView,大家定制的滑行视图类并从未落到实处任何方式的边界检查(bounds
checking)。图层内容极有非常的大大概滑出视图的边际并Infiniti滑下去。CAScrollLayer并未同样UIScrollView澳门葡京备用网址,中contentSize的属性,所以当CAScrollLayer滑动的时候完全未有一个大局的可滑动区域的定义,也无力回天自适应它的界线原点至你钦定的值。它因而不能自适应边界大小是因为它不要求,内容完全能够抢先界限。

那你势必会意外用CAScrollLayer的意思究竟何在,因为您能够大约地用一个常备的CALayer然后手动适应边界原点啊。真相其实并不复杂,UIScrollView并不曾用CAScrollLayer,事实上,便是简单的经过一向操作图层边界来贯彻滑动。

CAScrollLayer有二个诡秘的有用特色。假使您查看CAScrollLayer的头文件,你就会专注到有一个恢弘分类达成了一部分措施和总体性:

1 - (void)scrollPoint:(CGPoint)p;
2 - (void)scrollRectToVisible:(CGRect)r;
3 @property(readonly) CGRect visibleRect;

 

阅览这个方式和属性名,你只怕会以为这么些方法给每一个CALayer实例扩大了滑动成效。然而实际上他们只是放置在CAScrollLayer中的图层的实用方法。scrollPoint:措施从图层树中寻找并找到第三个可用的CAScrollLayer,然后滑动它使得内定点产生可视的。scrollRectToVisible:主意完成了一如既往的职业只可是是效能在三个矩形上的。visibleRect属性决定图层(若是存在的话)的哪部分是时下的可视区域。假如您本身完毕那几个格局就会相对轻易精晓有个别,可是CAScrollLayer帮您省了这几个麻烦,所以当提到到落到实处图层滑动的时候就能够用上了。

 

专用图层

复杂的组织都是专门化的

Catharine R. Stimpson

到近年来截止,我们早已研究过CALayer类了,同时大家也询问到了一部分13分有效的绘图和卡通片效果。但是Core
Animation图层不仅仅能功用于图片和颜料而已。本章就会学习别的的1部分图层类,进一步庞大使用Core
Animation绘图的技能。

复杂的团体都以专门化的
Catharine R. Stimpson

CAShapeLayer

在第陆章『视觉效果』大家学习到了不行使图片的气象下用CGPath去组织任意形状的黑影。如若大家能用一样的艺术创制一样形状的图层就好了。

CAShapeLayer是2个通过矢量图形而不是bitmap来绘制的图层子类。你钦赐诸如颜色和线宽等质量,用CGPath来定义想要绘制的图样,最终CAShapeLayer就自行渲染出来了。当然,你也足以用Core
Graphics直接向原始的CALyer的剧情中绘制多少个门路,相比较直下,使用CAShapeLayer有以下壹些亮点:

  • 渲染飞快。CAShapeLayer行使了硬件加快,绘制同一图形会比用Core
    Graphics快多数。
  • 快快使用内部存款和储蓄器。2个CAShapeLayer不需求像①般CALayer同1创制贰个投宿图形,所以不管有多大,都不会占用太多的内部存款和储蓄器。
  • 不会被图层边界剪开掉。二个CAShapeLayer能够在边界之外绘制。你的图层路线不会像在运用Core
    Graphics的一般性CALayer同壹被剪裁掉(如大家在第二章所见)。
  • 不会出现像素化。当您给CAShapeLayer做3D调换时,它不像八个有寄宿图的家常图层同样变得像素化。

到最近停止,我们早已搜求过CALayer类了,同时大家也领悟到了1些充裕管用的绘图和卡通效果。不过Core
Animation图层不仅仅能作用于图片和颜料而已。本章就会学习其余的1些图层类,进一步扩大使用Core
Animation绘图的手艺

创设2个CGPath

CAShapeLayer能够用来绘制全部能够通过CGPath来表示的样子。这些样子不自然要关掉,图层路线也不自然要不可破,事实上你能够在一个图层上制图好几个差异的形制。你能够决定一些性质比如lineWith(线宽,用点表示单位),lineCap(线条结尾的规范),和lineJoin(线条之间的结合点的金科玉律);可是在图层层面你只有2回机遇设置这几个属性。假使你想用区别颜色或风格来绘制八个样子,就只能为各种造型准备三个图层了。

清单六.壹的代码用二个CAShapeLayer渲染八个大致的火柴人。CAShapeLayer属性是CGPathRef品种,可是我们用UIBezierPath辅助类创造了图层路线,那样咱们就无须思虑人工释放CGPath了。图陆.一是代码运营的结果。固然还不是很圆满,但是到底知道了大要对吗!

清单6.1 用CAShapeLayer绘制二个火柴人

#import "DrawingView.h"
#import <QuartzCore/QuartzCore.h>

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *containerView;

@end

@implementation ViewController

- (void)viewDidLoad
{
  [super viewDidLoad];
  //create path
  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)];

  //create shape layer
  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;
  //add it to our view
  [self.containerView.layer addSublayer:shapeLayer];
}
@end

澳门葡京备用网址 5

图6.1

图6.1 用CAShapeLayer制图二个轻便的火柴人

  • #### CAShapeLayer

圆角

其次章里面涉及了CAShapeLayer为创立圆角视图提供了三个主意,就是CALayercornerRadius属性(译者注:其实是在第陆章提到的)。尽管选拔CAShapeLayer类须要越来越多的行事,但是它有3个优势正是足以独自钦赐每一种角。

大家创制圆角矩形其实就是人工绘制单独的直线和弧度,不过实际UIBezierPath有活动绘制圆角矩形的构造方法,下边这段代码绘制了一个有多个圆角五个直角的矩形:

//define path parameters
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作为视图的宿主图层,而不是增加三个子视图(图层蒙板的详细分解见第陆章『视觉效果』)。

在第陆章『视觉效果』大家学习到了不选拔图片的情状下用CGPath去社团任意形状的阴影。如若大家能用同样的不二等秘书诀创建一样形状的图层就好了。
CAShapeLayer是三个通过矢量图形而不是bitmap来绘制的图层子类。你内定诸如颜色和线宽等本性,用CGPath来定义想要绘制的图纸,最终CAShapeLayer就自行渲染出来了。当然,你也足以用Core
Graphics直接向原始的CALyer的内容中绘制一个门道,比较直下,使用CAShapeLayer有以下部分独到之处:

CATextLayer

用户分界面是心有余而力不足从二个单身的图纸里面营造的。多个企划能够的图标能够很好地显现四个开关或控件的来意,可是你迟早都要索要2个正确的老1套风格的公文标签。

只要您想在三个图层里面呈现文字,完全可以凭借图层代理直接将字符串使用Core
Graphics写入图层的剧情(这正是UILabel的精髓)。如若通过寄宿于图层的视图,直接在图层上操作,那其实一定繁琐。你要为每贰个出示文字的图层创设二个能像图层代理同样职业的类,还要逻辑上决断哪些图层须求出示哪个字符串,更别提还要记下差异的书体,颜色等1类别乱七八糟的东西。

有幸的是那些都以不须要的,Core
Animation提供了一个CALayer的子类CATextLayer,它以图层的样式包括了UILabel大致具有的绘图性格,并且额外提供了壹部分新的特征。

同样,CATextLayer也要比UILabel渲染得快得多。很少有人明白在iOS
陆及前面包车型客车本子,UILabel实际上是通过WebKit来实现绘制的,那样就形成了当有无数文字的时候就会有大幅的本性压力。而CATextLayer使用了Core
text,并且渲染得尤其快。

让我们来尝试用CATextLayer来体现壹些文字。清单陆.二的代码达成了那10%效,结果如图陆.二所示。

清单6.2 用CATextLayer来贯彻一个UILabel

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *labelView;

@end

@implementation ViewController
- (void)viewDidLoad
{
  [super viewDidLoad];

  //create a text layer
  CATextLayer *textLayer = [CATextLayer layer];
  textLayer.frame = self.labelView.bounds;
  [self.labelView.layer addSublayer:textLayer];

  //set text attributes
  textLayer.foregroundColor = [UIColor blackColor].CGColor;
  textLayer.alignmentMode = kCAAlignmentJustified;
  textLayer.wrapped = YES;

  //choose a font
  UIFont *font = [UIFont systemFontOfSize:15];

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

  //choose some text
  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";

  //set layer text
  textLayer.string = text;
}
@end

澳门葡京备用网址 6

图6.2

图6.2 用CATextLayer来展现一个纯文本标签

假使你精心看那些文件,你会意识2个突如其来的地方:那一个文件有部分像素化了。那是因为并不曾以Retina的措施渲染,第二章提到了那些contentScale属性,用来调节图层内容应当以什么样的分辨率来渲染。contentsScale并不珍重荧屏的拉伸因素而连日默以为1.0。倘使我们想以Retina的品质来呈现文字,大家就得手动地设置CATextLayercontentsScale属性,如下:

textLayer.contentsScale = [UIScreen mainScreen].scale;

这么就一举成功了这一个难点(如图六.三)

澳门葡京备用网址 7

图 6.3

图6.3 设置contentsScale来合营显示器

CATextLayerfont质量不是3个UIFont品类,而是三个CFTypeRef项目。这样能够遵照你的具体需求来决定字体属性应该是用CGFontRef种类只怕CTFontRef类型(Core
Text字体)。同时字体大小也是用fontSize属性单独设置的,因为CTFontRefCGFontRef并不像UIFont一样包蕴点大小。这几个事例会报告你怎么着将UIFont转换成CGFontRef

另外,CATextLayerstring属性并不是你想像的NSString类型,而是id种类。那样你既能够用NSString也足以用NSAttributedString来钦命文本了(注意,NSAttributedString并不是NSString的子类)。属性化字符串是iOS用来渲染字体风格的体制,它以一定的措施来支配钦命范围内的字符串的原本音信,比如字体,颜色,字重,斜体等。

  • 渲染飞快。CAShapeLayer使用了硬件加快,绘制同一图形会比用Core
    Graphics快繁多。
  • 迅猛利用内部存储器。贰个CAShapeLayer不要求像普通CALayer一样创设八个住宿图形,所以无论是有多大,都不会占用太多的内部存款和储蓄器。
  • 不会被图层边界剪开掉。一个CAShapeLayer能够在边界之外绘制。你的图层路径不会像在动用Core
    Graphics的日常CALayer同样被剪裁掉(如小编辈在其次章所见)。
  • 专用图层,CoreAnimation之专用图层。不会现出像素化。当您给CAShapeLayer做3D转移时,它不像3个有寄宿图的平凡图层同样变得像素化。

富文本

iOS
6中,Apple给UILabel和别的UIKit文本视图加多了直接的性格化字符串的支撑,应该说那是一个很有益的脾性。不超过实际在从iOS三.二初叶CATextLayer就早已支撑属性化字符串了。那样的话,假使你想要援助更低版本的iOS系统,CATextLayer毋庸讳言是您向分界面中追加富文本的好措施,而且也不用去跟复杂的Core
Text打交道,也省了用UIWebView的麻烦。

让我们编辑一下示范使用到NSAttributedString(见清单陆.三).iOS
陆及以上大家得以用新的NSTextAttributeName实例来安装大家的字符串属性,可是练习的目标是为了演示在iOS
5及以下,所以大家用了Core Text,也等于说你需求把Core Text
framework增加到您的项目中。不然,编写翻译器是力不从心识别属性常量的。

图6.四是代码运转结果(注意丰盛赫色的下划线文本)

清单陆.三 用NSAttributedString达成3个富文本标签。

#import "DrawingView.h"
#import <QuartzCore/QuartzCore.h>
#import <CoreText/CoreText.h>

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *labelView;

@end

@implementation ViewController

- (void)viewDidLoad
{
  [super viewDidLoad];

  //create a text layer
  CATextLayer *textLayer = [CATextLayer layer];
  textLayer.frame = self.labelView.bounds;
  textLayer.contentsScale = [UIScreen mainScreen].scale;
  [self.labelView.layer addSublayer:textLayer];

  //set text attributes
  textLayer.alignmentMode = kCAAlignmentJustified;
  textLayer.wrapped = YES;

  //choose a font
  UIFont *font = [UIFont systemFontOfSize:15];

  //choose some text
  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";

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

  //convert UIFont to a CTFont
  CFStringRef fontName = (__bridge CFStringRef)font.fontName;
  CGFloat fontSize = font.pointSize;
  CTFontRef fontRef = CTFontCreateWithName(fontName, fontSize, NULL);

  //set text attributes
  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)];

  //release the CTFont we created earlier
  CFRelease(fontRef);

  //set layer text
  textLayer.string = string;
}
@end

澳门葡京备用网址 8

图 6.4

图陆.四 用CATextLayer达成一个富文本标签。

创建三个CGPath
CAShapeLayer可以用来绘制全部能够通过CGPath来表示的形状。这几个样子不自然要关掉,图层路线也不自然要不可破,事实上你可以在三个图层上制图好几个差异的形象。你能够决定一些属性比如lineWith(线宽,用点表示单位),lineCap(线条结尾的指南),和lineJoin(线条之间的结合点的榜样);不过在图层层面你唯有一次机遇设置那一个属性。假设您想用差别颜色或风格来绘制八个形象,就只能为各类造型准备五个图层了。

行距和字距

有不可或缺提一下的是,由于绘制的贯彻机制不相同(Core
Text和WebKit),用CATextLayer渲染和用UILabel渲染出的文本行距和字距也不是不尽同样的。

两头的出入程度(由使用的书体和字符决定)总的来讲挺小,但是只要您想正确的突显普通便签和CATextLayer就必定要牢记那一点。

清单6.一的代码用一个CAShapeLayer渲染一个总结的火柴人。CAShapeLayer属性是CG帕特hRef类型,不过大家用UIBezierPath帮助类创造了图层路线,那样大家就绝不思考人工释放CGPath了。图6.壹是代码运维的结果。即便还不是很周详,可是到底知道了概略对吧!

UILabel的代替品

咱俩早就申明了CATextLayerUILabel不无更加好的品质表现,同时还有相当的布局选项并且在iOS
五上支撑富文本。可是与一般的标签相比较来讲会越发繁琐一些。借使大家真正在急需二个UILabel的可用代替品,最棒是力所能及在Interface
Builder上成立大家的标签,而且尽量地像相似的视图一样健康办事。

咱俩应当继承UILabel,然后加多2个子图层CATextLayer一碗水端平写显示文本的秘技。不过仍旧会有由UILabel-drawRect:艺术创造的空寄宿图。而且由于CALayer不支持电动缩放和机动布局,子视图并不是高歌猛进追踪视图边界的大大小小,所以每趟视图大小被改成,大家只好手动更新子图层的界线。

咱俩的确想要的是多少个用CATextLayer作为宿主图层的UILabel子类,那样就足以随着视图自动调整大小而且也尚未冗余的寄宿图啦。

就如大家在首先章『图层树』钻探的壹律,每三个UIView都以寄宿在2个CALayer的以身作则上。那几个图层是由视图自动创立和管制的,那大家得以用别的图层类型替代它么?一旦被创设,大家就不可能替代那几个图层了。不过只要咱们一而再了UIView,那我们就足以重写+layerClass情势使得在创设的时候能回去三个见仁见智的图层子类。UIView会在开头化的时候调用+layerClass艺术,然后用它的回到类型来创建宿主图层。

清单陆.四演示了二个UILabel子类LayerLabelCATextLayer制图它的主题材料,而不是调用一般的UILabel行使的较慢的-drawRect:方法。LayerLabel演示既能够用代码达成,也能够在Interface
Builder兑现,只要把平时的价签拖入视图之中,然后设置它的类是LayerLabel就足以了。

清单6.4 使用CATextLayerUILabel子类:LayerLabel

#import "LayerLabel.h"
#import <QuartzCore/QuartzCore.h>

@implementation LayerLabel
+ (Class)layerClass
{
  //this makes our label create a CATextLayer //instead of a regular CALayer for its backing layer
  return [CATextLayer class];
}

- (CATextLayer *)textLayer
{
  return (CATextLayer *)self.layer;
}

- (void)setUp
{
  //set defaults from UILabel settings
  self.text = self.text;
  self.textColor = self.textColor;
  self.font = self.font;

  //we should really derive these from the UILabel settings too
  //but that's complicated, so for now we'll just hard-code them
  [self textLayer].alignmentMode = kCAAlignmentJustified;

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

- (id)initWithFrame:(CGRect)frame
{
  //called when creating label programmatically
  if (self = [super initWithFrame:frame]) {
    [self setUp];
  }
  return self;
}

- (void)awakeFromNib
{
  //called when creating label using Interface Builder
  [self setUp];
}

- (void)setText:(NSString *)text
{
  super.text = text;
  //set layer text
  [self textLayer].string = text;
}

- (void)setTextColor:(UIColor *)textColor
{
  super.textColor = textColor;
  //set layer text color
  [self textLayer].foregroundColor = textColor.CGColor;
}

- (void)setFont:(UIFont *)font
{
  super.font = font;
  //set layer font
  CFStringRef fontName = (__bridge CFStringRef)font.fontName;
  CGFontRef fontRef = CGFontCreateWithFontName(fontName);
  [self textLayer].font = fontRef;
  [self textLayer].fontSize = font.pointSize;

  CGFontRelease(fontRef);
}
@end

如果你运维代码,你会发觉文本并未像素化,而笔者辈也尚未安装contentsScale属性。把CATextLayer作为宿主图层的另1功利就是视图自动安装了contentsScale属性。

在这些大致的事例中,我们只是完毕了UILabel的一部分风格和布局属性,不过有个别再改良一下我们就能够创立3个扶助UILabel具备机能照旧越多效益的LayerLabel类(你能够在某些线上的开源项目中找到)。

假定你打算援救iOS
6及以上,基于CATextLayer的价签大概就有些许局限性。然而总得来讲,假如想在app里面丰裕利用CALayer子类,用+layerClass来创造基于区别图层的视图是一个简练可复用的方法。

清单陆.一 用CAShapeLayer绘制1个火柴人
#import "DrawingView.h"
#import <QuartzCore/QuartzCore.h>

    @interface ViewController ()

    @property (nonatomic, weak) IBOutlet UIView *containerView;

    @end

    @implementation ViewController

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        //create path
        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)];

        //create shape layer
        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;
        //add it to our view
        [self.containerView.layer addSublayer:shapeLayer];
    }
    @end

    ```

![6.1.png](http://upload-images.jianshu.io/upload_images/1694376-7d3000c3c6b9099d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
**圆角**
 第二章里面提到了CAShapeLayer为创建圆角视图提供了一个方法,就是CALayer的cornerRadius属性(译者注:其实是在第四章提到的)。虽然使用CAShapeLayer类需要更多的工作,但是它有一个优势就是可以单独指定每个角。

我们创建圆角举行其实就是人工绘制单独的直线和弧度,但是事实上UIBezierPath有自动绘制圆角矩形的构造方法,下面这段代码绘制了一个有三个圆角一个直角的矩形:

//define path parameters
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作为视图的宿主图层,而不是添加一个子视图(图层蒙板的详细解释见第四章『视觉效果』)。

-  ####**CATextLayer**


用户界面是无法从一个单独的图片里面构建的。一个设计良好的图标能够很好地表现一个按钮或控件的意图,不过你迟早都要需要一个不错的老式风格的文本标签。
如果你想在一个图层里面显示文字,完全可以借助图层代理直接将字符串使用Core Graphics写入图层的内容(这就是UILabel的精髓)。如果越过寄宿于图层的视图,直接在图层上操作,那其实相当繁琐。你要为每一个显示文字的图层创建一个能像图层代理一样工作的类,还要逻辑上判断哪个图层需要显示哪个字符串,更别提还要记录不同的字体,颜色等一系列乱七八糟的东西。
万幸的是这些都是不必要的,Core Animation提供了一个CALayer的子类CATextLayer,它以图层的形式包含了UILabel
几乎所有的绘制特性,并且额外提供了一些新的特性。

同样,CATextLayer也要比UILabel渲染得快得多。很少有人知道在[iOS](http://lib.csdn.net/base/1) 6及之前的本,UILabel
其实是通过WebKit来实现绘制的,这样就造成了当有很多文字的时候就会有极大的性能压力。而CATextLayer使用了Core text,并且渲染得非常快。
让我们来尝试用CATextLayer来显示一些文字。清单6.2的代码实现了这一功能,结果如图6.2所示。
#####清单6.2 用CATextLayer来实现一个UILabel

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *labelView;

@end

@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];

    //create a text layer
    CATextLayer *textLayer = [CATextLayer layer];
    textLayer.frame = self.labelView.bounds;
    [self.labelView.layer addSublayer:textLayer];

    //set text attributes
    textLayer.foregroundColor = [UIColor blackColor].CGColor;
    textLayer.alignmentMode = kCAAlignmentJustified;
    textLayer.wrapped = YES;

    //choose a font
    UIFont *font = [UIFont systemFontOfSize:15];

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

    //choose some text
    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";

    //set layer text
    textLayer.string = text;
}
@end

![6.2.png](http://upload-images.jianshu.io/upload_images/1694376-3d0e66a31fbaa91b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
如果你仔细看这个文本,你会发现一个奇怪的地方:这些文本有一些像素化了。这是因为并没有以Retina的方式渲染,第二章提到了这个contentScale属性,用来决定图层内容应该以怎样的分辨率来渲染。contentsScale并不关心屏幕的拉伸因素而总是默认为1.0。如果我们想以Retina的质量来显示文字,我们就得手动地设置CATextLayer的contentsScale属性,如下:
`textLayer.contentsScale = [UIScreen mainScreen].scale;`

![6.3.png](http://upload-images.jianshu.io/upload_images/1694376-d646738dc883b658.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

         设置contentsScale来匹配屏幕
 CATextLayer的font属性不是一个UIFont类型,而是一个CFTypeRef类型。这样可以根据你的具体需要来决定字体属性应该是用CGFontRef类型还是CTFontRef类型(Core Text字体)。同时字体大小也是用fontSize属性单独设置的,因为CTFontRef和CGFontRef并不像UIFont一样包含点大小。这个例子会告诉你如何将UIFont转换成CGFontRef。

另外,CATextLayer的string属性并不是你想象的NSString类型,而是id类型。这样你既可以用NSString也可以用NSAttributedString来指定文本了(注意,NSAttributedString并不是NSString的子类)。属性化字符串是iOS用来渲染字体风格的机制,它以特定的方式来决定指定范围内的字符串的原始信息,比如字体,颜色,字重,斜体等。

**富文本**
 iOS 6中,Apple给UILabel和其他UIKit文本视图添加了直接的属性化字符串的支持,应该说这是一个很方便的特性。不过事实上从iOS3.2开始CATextLayer就已经支持属性化字符串了。这样的话,如果你想要支持更低版本的iOS系统,CATextLayer无疑是你向界面中增加富文本的好办法,而且也不用去跟复杂的Core Text打交道,也省了用UIWebView的麻烦。

让我们编辑一下示例使用到NSAttributedString(见清单6.3).iOS 6及以上我们可以用新的NSTextAttributeName实例来设置我们的字符串属性,但是练习的目的是为了演示在iOS 5及以下,所以我们用了Core Text,也就是说你需要把Core Text framework添加到你的项目中。否则,编译器是无法识别属性常量的。

图6.4是代码运行结果(注意那个红色的下划线文本)

####清单6.3 用NSAttributedString实现一个富文本标签。

CATransformLayer

当大家在构造复杂的3D事物的时候,假如能够组织独立成分就太便宜了。比如说,你想创设二个孩子的手臂:你就需求规定哪部分是男女的一手,哪一部分是亲骨血的膀子,哪一部分是子女的肘,哪部分是子女的膀子,哪一部分是儿女的肩头等等。

自然是同意单独地运动每个地区的呀。以肘为率掌握移动前臂和手,而不是肩膀。Core
Animation图层很轻松就能够让你在二D环境下做出这么的层级类别下的转换,不过3D情状下就不太恐怕,因为具备的图层都把她的孩子都平面化到一个情景中(第四章『转换』有涉嫌)。

CATransformLayer化解了那一个难点,CATransformLayer分歧于普通的CALayer,因为它无法显得它自个儿的内容。唯有当存在了一个能作用于子图层的改变它才真的存在。CATransformLayer并不平面化它的子图层,所以它能够用于组织八个层级的3D结构,比如作者的手臂示例。

用代码创立一个臂膀须求一定多的代码,所以本人就演示得更简短1些啊:在第四章的立方体示例,我们将因而旋转camara来化解图层平面化难题而不是像立方体示例代码中用的sublayerTransform。那是三个老大不错的技艺,可是只好成效域单个对象上,假使你的风貌包括多个立方,那大家就无法用这些技术单独旋转他们了。

那么,就让我们来试一试CATransformLayer啊,第二个难题就来了:在第四章,大家是用多个视图来布局了我们的立方体,而不是单身的图层。我们不能够在不打乱已部分视图层次的前提下在一个自家不是有寄宿图的图层中放置多个寄宿图图层。我们能够创制三个新的UIView子类寄宿在CATransformLayer(用+layerClass措施)之上。可是,为了简化案例,我们只是重建了一个独门的图层,而不是运用视图。这意味大家不能够像第4章同样在立方体表面展现开关和标签,可是大家前些天也用不到那个脾性。

清单六.五就是代码。大家以大家在第五章使用过的同等基本逻辑放置立方体。不过并不像在此以前那样直接将立方面增多到容器视图的宿主图层,我们将她们放置到八个CATransformLayer中创设1个独门的立方体对象,然后将多少个如此的立方体放进容器中。我们随便地给立方面染色以将他们区分开来,那样就毫无靠标签也许光亮来分别他们。图6.5是运作结果。

清单6.5 用CATransformLayer装配一个3D图层体系

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *containerView;

@end

@implementation ViewController

- (CALayer *)faceWithTransform:(CATransform3D)transform
{
  //create cube face layer
  CALayer *face = [CALayer layer];
  face.frame = CGRectMake(-50, -50, 100, 100);

  //apply a random color
  CGFloat red = (rand() / (double)INT_MAX);
  CGFloat green = (rand() / (double)INT_MAX);
  CGFloat blue = (rand() / (double)INT_MAX);
  face.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;

  //apply the transform and return
  face.transform = transform;
  return face;
}

- (CALayer *)cubeWithTransform:(CATransform3D)transform
{
  //create cube layer
  CATransformLayer *cube = [CATransformLayer layer];

  //add cube face 1
  CATransform3D ct = CATransform3DMakeTranslation(0, 0, 50);
  [cube addSublayer:[self faceWithTransform:ct]];

  //add cube face 2
  ct = CATransform3DMakeTranslation(50, 0, 0);
  ct = CATransform3DRotate(ct, M_PI_2, 0, 1, 0);
  [cube addSublayer:[self faceWithTransform:ct]];

  //add cube face 3
  ct = CATransform3DMakeTranslation(0, -50, 0);
  ct = CATransform3DRotate(ct, M_PI_2, 1, 0, 0);
  [cube addSublayer:[self faceWithTransform:ct]];

  //add cube face 4
  ct = CATransform3DMakeTranslation(0, 50, 0);
  ct = CATransform3DRotate(ct, -M_PI_2, 1, 0, 0);
  [cube addSublayer:[self faceWithTransform:ct]];

  //add cube face 5
  ct = CATransform3DMakeTranslation(-50, 0, 0);
  ct = CATransform3DRotate(ct, -M_PI_2, 0, 1, 0);
  [cube addSublayer:[self faceWithTransform:ct]];

  //add cube face 6
  ct = CATransform3DMakeTranslation(0, 0, -50);
  ct = CATransform3DRotate(ct, M_PI, 0, 1, 0);
  [cube addSublayer:[self faceWithTransform:ct]];

  //center the cube layer within the container
  CGSize containerSize = self.containerView.bounds.size;
  cube.position = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);

  //apply the transform and return
  cube.transform = transform;
  return cube;
}

- (void)viewDidLoad
{
  [super viewDidLoad];

  //set up the perspective transform
  CATransform3D pt = CATransform3DIdentity;
  pt.m34 = -1.0 / 500.0;
  self.containerView.layer.sublayerTransform = pt;

  //set up the transform for cube 1 and add it
  CATransform3D c1t = CATransform3DIdentity;
  c1t = CATransform3DTranslate(c1t, -100, 0, 0);
  CALayer *cube1 = [self cubeWithTransform:c1t];
  [self.containerView.layer addSublayer:cube1];

  //set up the transform for cube 2 and add it
  CATransform3D c2t = CATransform3DIdentity;
  c2t = CATransform3DTranslate(c2t, 100, 0, 0);
  c2t = CATransform3DRotate(c2t, -M_PI_4, 1, 0, 0);
  c2t = CATransform3DRotate(c2t, -M_PI_4, 0, 1, 0);
  CALayer *cube2 = [self cubeWithTransform:c2t];
  [self.containerView.layer addSublayer:cube2];
}
@end

澳门葡京备用网址 9

图 6.5

图陆.五 同一视角下的俩例外转换的立方体

import “DrawingView.h”

CAGradientLayer

CAGradientLayer是用来生成三种或越来越多颜色平滑渐变的。用Core
Graphics复制三个CAGradientLayer并将内容绘制到三个常常图层的寄宿图也是有望的,不过CAGradientLayer的着实好处在于绘制使用了硬件加速。

import <QuartzCore/QuartzCore.h>

基础渐变

我们将从1个简易的红变蓝的对角线渐变初步(见清单陆.陆).这几个渐变色彩放在2个数组中,并赋给colors质量。那个数组成员接受CGColorRef品类的值(并不是从NSObject派生而来),所以我们要用通过bridge转变以保险编写翻译符合规律。

CAGradientLayer也有startPointendPoint属性,他们垄断了渐变的矛头。那多个参数是以单位坐标系实行的概念,所以左上角坐标是{0,
0},右下角坐标是{一, 一}。代码运维结果如图陆.陆

清单六.陆 轻巧的三种颜色的对角线渐变

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *containerView;

@end

@implementation ViewController

- (void)viewDidLoad
{
  [super viewDidLoad];
  //create gradient layer and add it to our container view
  CAGradientLayer *gradientLayer = [CAGradientLayer layer];
  gradientLayer.frame = self.containerView.bounds;
  [self.containerView.layer addSublayer:gradientLayer];

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

  //set gradient start and end points
  gradientLayer.startPoint = CGPointMake(0, 0);
  gradientLayer.endPoint = CGPointMake(1, 1);
}
@end

澳门葡京备用网址 10

图 6.6

图6.6 用CAGradientLayer金镶玉裹福禄双全轻易的三种颜色的对角线渐变

import <CoreText/CoreText.h>

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *labelView;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    //create a text layer
    CATextLayer *textLayer = [CATextLayer layer];
    textLayer.frame = self.labelView.bounds;
    textLayer.contentsScale = [UIScreen mainScreen].scale;
    [self.labelView.layer addSublayer:textLayer];

    //set text attributes
    textLayer.alignmentMode = kCAAlignmentJustified;
    textLayer.wrapped = YES;

    //choose a font
    UIFont *font = [UIFont systemFontOfSize:15];

    //choose some text
    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";

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

    //convert UIFont to a CTFont
    CFStringRef fontName = (__bridge CFStringRef)font.fontName;
    CGFloat fontSize = font.pointSize;
    CTFontRef fontRef = CTFontCreateWithName(fontName, fontSize, NULL);

    //set text attributes
    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)];

    //release the CTFont we created earlier
    CFRelease(fontRef);

    //set layer text
    textLayer.string = string;
}
@end
```

澳门葡京备用网址 11

6.4.png

行距和字距

有必不可缺提一下的是,由于绘制的达成机制区别(Core
Text和Web基特),用CATextLayer渲染和用UILabel渲染出的文书行距和字距也不是不尽同样的。

二者的差异程度(由使用的书体和字符决定)总的来讲挺小,不过如若您想正确的显示普通便签和CATextLayer就必然要铭记那或多或少

UILabel的取代品
咱俩已经注明了CATextLayer比UILabel有着越来越好的习性表现,同时还有额外的布局选项并且在iOS
5上帮衬富文本。可是与壹般的竹签比较来说会愈来愈繁琐1些。假诺我们实在在须求3个UILabel的可用代替品,最佳是力所能及在Interface
Builder上开创我们的价签,而且尽量地像相似的视图同样健康工作。

大家应有承袭UILabel,然后增添多个子图层CATextLayer同等看待写显示文本的措施。不过依然会有由UILabel的-drawRect:方法创设的空寄宿图。而且由于CALayer不援助自动缩放和自动布局,子视图并不是积极追踪视图边界的尺寸,所以每一遍视图大小被更动,我们不得不手动更新子图层的边际。

咱俩真的想要的是二个用CATextLayer作为宿主图层的UILabel子类,那样就能够趁机视图自动调控大小而且也尚无冗余的寄宿图啦。

就像是大家在率先章『图层树』切磋的1致,每一个UIView都以借宿在三个CALayer的以身作则上。那一个图层是由视图自动创设和管制的,这大家得以用其余图层类型代替他么?1旦被创造,我们就无法代替这么些图层了。但是1旦大家延续了UIView,那大家就能够重写+layerClass方法使得在开立的时候能回到2个见仁见智的图层子类。UIView会在初步化的时候调用+layerClass方法,然后用它的归来类型来创设宿主图层。

清单陆.4演示了贰个UILabel子类LayerLabel用CATextLayer绘制它的标题,而不是调用一般的UILabel使用的较慢的-drawRect:方法。LayerLabel示例既可以用代码完毕,也可以在Interface
Builder兑现,只要把平常的价签拖入视图之中,然后设置它的类是LayerLabel就足以了。

多重渐变

假定您愿意,colors属性能够分包众多颜色,所以创设三个彩虹一样的多种渐变也是很粗大略的。暗中同意景况下,这几个颜色在上空上均匀地被渲染,然则大家能够用locations本性来调动空间。locations品质是三个浮点数值的数组(以NSNumber装进)。那些浮点数定义了colors属性中种种不一样颜色的职位,同样的,也是以单位坐标系进行标定。0.0意味着着渐变的始发,1.0意味着截至。

locations数组并不是挟持供给的,可是一旦你给它赋值了就断定要确定保障locations的数组大小和colors数组大小一定要平等,不然你将会博得三个空白的渐变。

清单6.柒来得了贰个基于清单陆.陆的对角线渐变的代码改动。今后改为了从红到黄最终到洋红的渐变。locations数组内定了0.0,0.2伍和0.五多少个数值,那样那八个渐变就有点像挤在了左上角。(如图6.七).

清单6.七 在潜移默化上选择locations

- (void)viewDidLoad {
    [super viewDidLoad];

    //create gradient layer and add it to our container view
    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
    gradientLayer.frame = self.containerView.bounds;
    [self.containerView.layer addSublayer:gradientLayer];

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

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

    //set gradient start and end points
    gradientLayer.startPoint = CGPointMake(0, 0);
    gradientLayer.endPoint = CGPointMake(1, 1);
}

澳门葡京备用网址 12

图 6.7

图6.7 用locations组织偏移至左上角的三色渐变

清单6.4 使用CATextLayer的UILabel子类:LayerLabel
#import "LayerLabel.h"
#import <QuartzCore/QuartzCore.h>

    @implementation LayerLabel
    + (Class)layerClass
    {
        //this makes our label create a CATextLayer //instead of a regular CALayer for its backing layer
        return [CATextLayer class];
    }

    - (CATextLayer *)textLayer
    {
        return (CATextLayer *)self.layer;
    }

    - (void)setUp
    {
        //set defaults from UILabel settings
        self.text = self.text;
        self.textColor = self.textColor;
        self.font = self.font;

        //we should really derive these from the UILabel settings too
        //but that's complicated, so for now we'll just hard-code them
        [self textLayer].alignmentMode = kCAAlignmentJustified;

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

    - (id)initWithFrame:(CGRect)frame
    {
        //called when creating label programmatically
        if (self = [super initWithFrame:frame]) {
            [self setUp];
        }
        return self;
    }

    - (void)awakeFromNib
    {
        //called when creating label using Interface Builder
        [self setUp];
    }

    - (void)setText:(NSString *)text
    {
        super.text = text;
        //set layer text
        [self textLayer].string = text;
    }

    - (void)setTextColor:(UIColor *)textColor
    {
        super.textColor = textColor;
        //set layer text color
        [self textLayer].foregroundColor = textColor.CGColor;
    }

    - (void)setFont:(UIFont *)font
    {
        super.font = font;
        //set layer font
        CFStringRef fontName = (__bridge CFStringRef)font.fontName;
        CGFontRef fontRef = CGFontCreateWithFontName(fontName);
        [self textLayer].font = fontRef;
        [self textLayer].fontSize = font.pointSize;

        CGFontRelease(fontRef);
    }
    @end

设若你运转代码,你会意识文本并未像素化,而小编辈也尚无设置contentsScale属性。把CATextLayer作为宿主图层的另一益处正是视图自动安装了contentsScale属性。

在那个轻巧的例子中,大家只是达成了UILabel的壹有的风格和布局属性,但是有点再改正一下大家就足以成立1个支撑UILabel全部作用竟然愈多职能的LayerLabel类(你能够在局地线上的开源项目中找到)。

假如你打算协助iOS
六及以上,基于CATextLayer的标签恐怕就有个别许局限性。不过总得来讲,假若想在app里面充裕利用CALayer子类,用+layerClass来创设基于不相同图层的视图是三个轻松可复用的措施。

  • #### CATransformLayer

当大家在组织复杂的3D事物的时候,假如能够协会独立成分就太方便了。比如说,你想成立一个男女的膀子:你就须要明确哪1部分是亲骨血的招数,哪部分是子女的膀子,哪部分是子女的肘,哪一部分是儿女的膀子,哪部分是孩子的肩膀等等。

本来是允许单独地移动每个地方的呐。以肘为指点会活动前臂和手,而不是肩膀。Core
Animation图层很轻便就可以让你在二D条件下做出那样的层级种类下的调换,然则3D情形下就不太大概,因为有着的图层都把他的男女都平面化到一个光景中(第4章『转换』有提到)。

CATransformLayer化解了那个主题材料,CATransformLayer分化于一般的CALayer,因为它无法显得它本人的剧情。唯有当存在了一个能功效域子图层的改换它才真的存在。CATransformLayer并不平面化它的子图层,所以它能够用于协会3个层级的3D结构,比如作者的上肢示例。

用代码创制一个臂膀须求极度多的代码,所以本人就演示得更简约一些啊:在第四章的立方体示例,大家将通过旋转camara来缓解图层平面化难点而不是像立方体示例代码中用的sublayerTransform。那是二个分外不错的本领,可是只好功效域单个对象上,假设您的气象包涵四个立方,那大家就不能够用这一个能力单独旋转他们了。

那就是说,就让大家来试1试CATransformLayer吧,第1个难点就来了:在第5章,大家是用多少个视图来协会了大家的立方体,而不是独自的图层。我们不可能在不打乱已某些视图层次的前提下在1个本身不是有寄宿图的图层中放置1个寄宿图图层。大家得以创制一个新的UIView子类寄宿在CATransformLayer(用+layerClass方法)之上。然则,为了简化案例,大家只有重建了二个独自的图层,而不是利用视图。那表示大家不可能像第6章一样在立方体表面彰显按键和标签,不过我们未来也用不到这几个本性。

清单6.伍正是代码。大家以大家在第伍章使用过的等同基本逻辑放置立方体。不过并不像在此之前那么直接将立方面增加到容器视图的宿主图层,大家将他们放置到3个CATransformLayer中成立3个单独的立方体对象,然后将五个这么的立方体放进容器中。大家随便地给立方面染色以将她们分别开来,那样就绝不靠标签只怕光亮来区分他们。图6.5是运作结果。

CAReplicatorLayer

CAReplicatorLayer的指标是为着快捷调换诸多貌似的图层。它会绘制一个或八个图层的子图层,并在各种复制体上使用分化的更动。看上去演示能够更进一步解释这一个,大家来写个例证吗。

清单陆.伍 用CATransformLayer装配2个3D图层种类
    @interface ViewController ()

    @property (nonatomic, weak) IBOutlet UIView *containerView;

    @end

    @implementation ViewController

    - (CALayer *)faceWithTransform:(CATransform3D)transform
    {
        //create cube face layer
        CALayer *face = [CALayer layer];
        face.frame = CGRectMake(-50, -50, 100, 100);

        //apply a random color
        CGFloat red = (rand() / (double)INT_MAX);
        CGFloat green = (rand() / (double)INT_MAX);
        CGFloat blue = (rand() / (double)INT_MAX);
        face.backgroundColor = [UIColor colorWithRed:red green:green blue:blue alpha:1.0].CGColor;

        //apply the transform and return
        face.transform = transform;
        return face;
    }

    - (CALayer *)cubeWithTransform:(CATransform3D)transform
    {
        //create cube layer
        CATransformLayer *cube = [CATransformLayer layer];

        //add cube face 1
        CATransform3D ct = CATransform3DMakeTranslation(0, 0, 50);
        [cube addSublayer:[self faceWithTransform:ct]];

        //add cube face 2
        ct = CATransform3DMakeTranslation(50, 0, 0);
        ct = CATransform3DRotate(ct, M_PI_2, 0, 1, 0);
        [cube addSublayer:[self faceWithTransform:ct]];

        //add cube face 3
        ct = CATransform3DMakeTranslation(0, -50, 0);
        ct = CATransform3DRotate(ct, M_PI_2, 1, 0, 0);
        [cube addSublayer:[self faceWithTransform:ct]];

        //add cube face 4
        ct = CATransform3DMakeTranslation(0, 50, 0);
        ct = CATransform3DRotate(ct, -M_PI_2, 1, 0, 0);
        [cube addSublayer:[self faceWithTransform:ct]];

        //add cube face 5
        ct = CATransform3DMakeTranslation(-50, 0, 0);
        ct = CATransform3DRotate(ct, -M_PI_2, 0, 1, 0);
        [cube addSublayer:[self faceWithTransform:ct]];

        //add cube face 6
        ct = CATransform3DMakeTranslation(0, 0, -50);
        ct = CATransform3DRotate(ct, M_PI, 0, 1, 0);
        [cube addSublayer:[self faceWithTransform:ct]];

        //center the cube layer within the container
        CGSize containerSize = self.containerView.bounds.size;
        cube.position = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0);

        //apply the transform and return
        cube.transform = transform;
        return cube;
    }

    - (void)viewDidLoad
    {
        [super viewDidLoad];

        //set up the perspective transform
        CATransform3D pt = CATransform3DIdentity;
        pt.m34 = -1.0 / 500.0;
        self.containerView.layer.sublayerTransform = pt;

        //set up the transform for cube 1 and add it
        CATransform3D c1t = CATransform3DIdentity;
        c1t = CATransform3DTranslate(c1t, -100, 0, 0);
        CALayer *cube1 = [self cubeWithTransform:c1t];
        [self.containerView.layer addSublayer:cube1];

        //set up the transform for cube 2 and add it
        CATransform3D c2t = CATransform3DIdentity;
        c2t = CATransform3DTranslate(c2t, 100, 0, 0);
        c2t = CATransform3DRotate(c2t, -M_PI_4, 1, 0, 0);
        c2t = CATransform3DRotate(c2t, -M_PI_4, 0, 1, 0);
        CALayer *cube2 = [self cubeWithTransform:c2t];
        [self.containerView.layer addSublayer:cube2];
    }
    @end

    - (void)viewDidLoad
    {
        [super viewDidLoad];

        //set up the perspective transform
        CATransform3D pt = CATransform3DIdentity;
        pt.m34 = -1.0 / 500.0;
        self.containerView.layer.sublayerTransform = pt;

        //set up the transform for cube 1 and add it
        CATransform3D c1t = CATransform3DIdentity;
        c1t = CATransform3DTranslate(c1t, -100, 0, 0);
        CALayer *cube1 = [self cubeWithTransform:c1t];
        [self.containerView.layer addSublayer:cube1];

        //set up the transform for cube 2 and add it
        CATransform3D c2t = CATransform3DIdentity;
        c2t = CATransform3DTranslate(c2t, 100, 0, 0);
        c2t = CATransform3DRotate(c2t, -M_PI_4, 1, 0, 0);
        c2t = CATransform3DRotate(c2t, -M_PI_4, 0, 1, 0);
        CALayer *cube2 = [self cubeWithTransform:c2t];
        [self.containerView.layer addSublayer:cube2];
    }
    @end

    - (void)viewDidLoad
    {
        [super viewDidLoad];

        //set up the perspective transform
        CATransform3D pt = CATransform3DIdentity;
        pt.m34 = -1.0 / 500.0;
        self.containerView.layer.sublayerTransform = pt;

        //set up the transform for cube 1 and add it
        CATransform3D c1t = CATransform3DIdentity;
        c1t = CATransform3DTranslate(c1t, -100, 0, 0);
        CALayer *cube1 = [self cubeWithTransform:c1t];
        [self.containerView.layer addSublayer:cube1];

        //set up the transform for cube 2 and add it
        CATransform3D c2t = CATransform3DIdentity;
        c2t = CATransform3DTranslate(c2t, 100, 0, 0);
        c2t = CATransform3DRotate(c2t, -M_PI_4, 1, 0, 0);
        c2t = CATransform3DRotate(c2t, -M_PI_4, 0, 1, 0);
        CALayer *cube2 = [self cubeWithTransform:c2t];
        [self.containerView.layer addSublayer:cube2];
    }
    @end

澳门葡京备用网址 13

6.5.png

  • #### *CAGradientLayer

CAGradientLayer是用来生成二种或愈多颜色平滑渐变的。用Core
Graphics复制八个CAGradientLayer并将内容绘制到三个常备图层的寄宿图也是有望的,但是CAGradientLayer的着实好处在于绘制使用了硬件加快。

基础渐变

我们将从几个轻巧的红变蓝的对角线渐变早先(见清单6.6).那几个渐变色彩放在二个数组中,并赋给colors属性。这些数组成员接受CGColorRef类型的值(并不是从NSObject派生而来),所以大家要用通过bridge转变以担保编写翻译平时。

CAGradientLayer也有startPoint和endPoint属性,他们说了算了渐变的自由化。那多个参数是以单位坐标系实行的定义,所以左上角坐标是{0,
0},右下角坐标是{一, 一}。代码运营结果如图陆.6

双重图层(Repeating Layers)

清单六.第88中学,我们在显示器的中档成立了二个小玳瑁红方块图层,然后用CAReplicatorLayer扭转10个图层组成一个圆形。instanceCount性子钦点了图层供给重新多少次。instanceTransform钦点了1个CATransform3D3D调换(那种场馆下,下一图层的位移和旋转将会活动到圆圈的下二个点)。

调换是逐级充实的,每种实例都是相持于前一实例布局。那正是怎么这几个复制体最终不会产出在同意地方上,图陆.捌是代码运转结果。

清单6.8 用CAReplicatorLayer重复图层

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *containerView;

@end

@implementation ViewController
- (void)viewDidLoad
{
    [super viewDidLoad];
    //create a replicator layer and add it to our view
    CAReplicatorLayer *replicator = [CAReplicatorLayer layer];
    replicator.frame = self.containerView.bounds;
    [self.containerView.layer addSublayer:replicator];

    //configure the replicator
    replicator.instanceCount = 10;

    //apply a transform for each instance
    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;

    //apply a color shift for each instance
    replicator.instanceBlueOffset = -0.1;
    replicator.instanceGreenOffset = -0.1;

    //create a sublayer and place it inside the replicator
    CALayer *layer = [CALayer layer];
    layer.frame = CGRectMake(100.0f, 100.0f, 100.0f, 100.0f);
    layer.backgroundColor = [UIColor whiteColor].CGColor;
    [replicator addSublayer:layer];
}
@end

澳门葡京备用网址 14

图 6.8

图6.8 用CAReplicatorLayer开创1圈图层

小心到当图层在重复的时候,他们的水彩也在更改:那是用instanceBlueOffsetinstanceGreenOffset质量完成的。通过逐步缩减茄皮紫和青古铜色通道,大家逐步将图层颜色转变来了水晶绿。那些复制效果看起来很酷,不过CAReplicatorLayer诚然使用到骨子里程序上的场景比如:2个游戏中程导弹弹的轨道云,恐怕粒子爆炸(尽管iOS
5已经引进了CAEmitterLayer,它更适合创立任意的粒子效果)。除却,还有三个实际行使是:反射。

清单陆.陆 简单的二种颜色的对角线渐变
 @interface ViewController ()

    @property (nonatomic, weak) IBOutlet UIView *containerView;

    @end

    @implementation ViewController

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        //create gradient layer and add it to our container view
        CAGradientLayer *gradientLayer = [CAGradientLayer layer];
        gradientLayer.frame = self.containerView.bounds;
        [self.containerView.layer addSublayer:gradientLayer];

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

        //set gradient start and end points
        gradientLayer.startPoint = CGPointMake(0, 0);
        gradientLayer.endPoint = CGPointMake(1, 1);
    }
    @end
    ```

![6.6.png](http://upload-images.jianshu.io/upload_images/1694376-9d6f27a9846fb2b1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

####多重渐变
 如果你愿意,colors属性可以包含很多颜色,所以创建一个彩虹一样的多重渐变也是很简单的。默认情况下,这些颜色在空间上均匀地被渲染,但是我们可以用locations属性来调整空间。locations属性是一个浮点数值的数组(以NSNumber包装)。这些浮点数定义了colors属性中每个不同颜色的位置,同样的,也是以单位坐标系进行标定。0.0代表着渐变的开始,1.0代表着结束。

locations数组并不是强制要求的,但是如果你给它赋值了就一定要确保locations的数组大小和colors数组大小一定要相同,否则你将会得到一个空白的渐变。

清单6.7展示了一个基于清单6.6的对角线渐变的代码改造。现在变成了从红到黄最后到绿色的渐变。locations数组指定了0.0,0.25和0.5三个数值,这样这三个渐变就有点像挤在了左上角。(如图6.7).

#####清单6.7 在渐变上使用locations

- (void)viewDidLoad {
    [super viewDidLoad];

    //create gradient layer and add it to our container view
    CAGradientLayer *gradientLayer = [CAGradientLayer layer];
    gradientLayer.frame = self.containerView.bounds;
    [self.containerView.layer addSublayer:gradientLayer];

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

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

    //set gradient start and end points
    gradientLayer.startPoint = CGPointMake(0, 0);
    gradientLayer.endPoint = CGPointMake(1, 1);
}
```

澳门葡京备用网址 15

6.7.png

  • #### CAReplicatorLayer

CAReplicatorLayer的指标是为着急速调换多数貌似的图层。它会绘制多个或多少个图层的子图层,并在各样复制体上使用区别的改换。看上去演示能够进一步解释那一个,我们来写个例证吗。

反射

使用CAReplicatorLayer并利用二个负比例调换于一个复制图层,你就足以创设钦定视图(或任何视图层次)内容的镜像图片,那样就创办了二个实时的『反射』效果。让我们来尝试完毕那一个创新意识:内定二个持续于UIViewReflectionView,它会活动发出内容的反射功能。实现这些功用的代码异常的粗略(见清单六.9),实际上用ReflectionView金玉锦绣这些效果会更简短,我们只供给把ReflectionView的实例放置于Interface
Builder(见图陆.九),它就会实时生成子视图的反光,而不必要其余代码(见图陆.十).

清单6.9 用CAReplicatorLayer活动绘制反射

#import "ReflectionView.h"
#import <QuartzCore/QuartzCore.h>

@implementation ReflectionView

+ (Class)layerClass
{
    return [CAReplicatorLayer class];
}

- (void)setUp
{
    //configure replicator
    CAReplicatorLayer *layer = (CAReplicatorLayer *)self.layer;
    layer.instanceCount = 2;

    //move reflection instance below original and flip vertically
    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;

    //reduce alpha of reflection layer
    layer.instanceAlphaOffset = -0.6;
}

- (id)initWithFrame:(CGRect)frame
{
    //this is called when view is created in code
    if ((self = [super initWithFrame:frame])) {
        [self setUp];
    }
    return self;
}

- (void)awakeFromNib
{
    //this is called when view is created from a nib
    [self setUp];
}
@end

澳门葡京备用网址 16

图 6.9

图6.9 在Interface Builder中使用ReflectionView

澳门葡京备用网址 17

图 6.10

图6.10 ReflectionView自行实时爆发反射功效。

开源代码ReflectionView成就了1个自适应的渐变淡出效果(用CAGradientLayer和图层蒙板完成),代码见
https://github.com/nicklockwood/ReflectionView

再度图层(Repeating Layers)

清单陆.第88中学,大家在显示器的中等创设了一个小浅绿灰方块图层,然后用CAReplicatorLayer生成11个图层组成2个圆形。instanceCount属性内定了图层须要再一次多少次。instanceTransform内定了贰个CATransform3D3D转移(那种情形下,下一图层的移动和旋转将会活动到圆圈的下二个点)。

转变是稳步扩充的,每一种实例都以对峙于前一实例布局。那就是干吗那些复制体最后不会并发在允许地点上,图陆.八是代码运转结果。

清单6.八 用CAReplicatorLayer重复图层

   @interface ViewController ()

    @property (nonatomic, weak) IBOutlet UIView *containerView;

    @end

    @implementation ViewController
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        //create a replicator layer and add it to our view
        CAReplicatorLayer *replicator = [CAReplicatorLayer layer];
        replicator.frame = self.containerView.bounds;
        [self.containerView.layer addSublayer:replicator];

        //configure the replicator
        replicator.instanceCount = 10;

        //apply a transform for each instance
        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;

        //apply a color shift for each instance
        replicator.instanceBlueOffset = -0.1;
        replicator.instanceGreenOffset = -0.1;

        //create a sublayer and place it inside the replicator
        CALayer *layer = [CALayer layer];
        layer.frame = CGRectMake(100.0f, 100.0f, 100.0f, 100.0f);
        layer.backgroundColor = [UIColor whiteColor].CGColor;
        [replicator addSublayer:layer];
    }
    @end

澳门葡京备用网址 18

6.8.png

留意到当图层在再度的时候,他们的颜色也在转移:那是用instanceBlueOffset和instance格林Offset属性达成的。通过逐级调整和减少深牡蛎白和高粱红通道,大家日益将图层颜色转换到了革命。那一个复制效果看起来很酷,不过CAReplicatorLayer真正应用到实际程序上的场合比如:2个娱乐中程导弹弹的轨道云,只怕粒子爆炸(尽管iOS
五已经引进了CAEmitterLayer,它更合乎创造任意的粒子效果)。除却,还有三个其进行使是:反射。

CAScrollLayer

对于1个未改动的图层,它的bounds和它的frame是如出一辙的,frame特性是由bounds品质自动总结而出的,所以更换任意三个值都会更新任何值。

但是假诺你只想体现一个大图层里面包车型大巴一小部分呢。比如说,你或然有三个异常的大的图片,你指望用户能够轻巧滑动,恐怕是2个数据或文本的长列表。在三个独立的iOS应用中,你恐怕会用到UITableView或是UIScrollView,不过对于单身的图层来讲,什么会等价于刚先生刚提到的UITableViewUIScrollView呢?

在其次章中,我们追究了图层的contentsRect本性的用法,它确实是力所能及消除在图层中小地点显得大图片的缓解措施。可是1旦您的图层包罗子图层那它就不是多个足够好的消除方案,因为,那样做的话每一次你想『滑动』可视区域的时候,你就要求手工重新总结并立异具有的子图层地点。

以此时候就必要CAScrollLayer了。CAScrollLayer有一个-scrollToPoint:格局,它自动适应bounds的原点以便图层内容出未来滑行的地点。注意,那便是它做的享有工作。后边提到过,Core
Animation并不处理用户输入,所以CAScrollLayer并不承担将触摸事件转变为滑行事件,既不渲染滚动条,也不落到实处任何iOS内定行为例如滑动反弹(当视图滑动超多了它的边界的将会反弹回正确的地方)。

让大家来用CAScrollLayer来科学普及2个为主的UIScrollView替代品。大家将会用CAScrollLayer用作视图的宿主图层,并创造1个自定义的UIView,然后用UIPanGestureRecognizer兑现触摸事件响应。这段代码见清单6.10.
图陆.1壹是运作效果:ScrollView突显了一个大于它的frameUIImageView

清单6.10 用CAScrollLayer贯彻滑动视图

#import "ScrollView.h"
#import <QuartzCore/QuartzCore.h> @implementation ScrollView
+ (Class)layerClass
{
    return [CAScrollLayer class];
}

- (void)setUp
{
    //enable clipping
    self.layer.masksToBounds = YES;

    //attach pan gesture recognizer
    UIPanGestureRecognizer *recognizer = nil;
    recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    [self addGestureRecognizer:recognizer];
}

- (id)initWithFrame:(CGRect)frame
{
    //this is called when view is created in code
    if ((self = [super initWithFrame:frame])) {
        [self setUp];
    }
    return self;
}

- (void)awakeFromNib {
    //this is called when view is created from a nib
    [self setUp];
}

- (void)pan:(UIPanGestureRecognizer *)recognizer
{
    //get the offset by subtracting the pan gesture
    //translation from the current bounds origin
    CGPoint offset = self.bounds.origin;
    offset.x -= [recognizer translationInView:self].x;
    offset.y -= [recognizer translationInView:self].y;

    //scroll the layer
    [(CAScrollLayer *)self.layer scrollToPoint:offset];

    //reset the pan gesture translation
    [recognizer setTranslation:CGPointZero inView:self];
}
@end

澳门葡京备用网址 19

图 6.11

图6.11 用UIScrollView创造八个成团的滑行视图

不同于UIScrollView,大家定制的滑动视图类并不曾兑现任何款式的界线检查(bounds
checking)。图层内容极有十分大或者滑出视图的分界并可是滑下去。CAScrollLayer并从未同样UIScrollViewcontentSize的属性,所以当CAScrollLayer滑动的时候完全未有1个大局的可滑动区域的概念,也无能为力自适应它的界限原点至你钦命的值。它之所以无法自适应边界大小是因为它不必要,内容完全能够超过界限。

那您鲜明会奇异用CAScrollLayer的意思究竟何在,因为你可以省略地用两个平凡的CALayer接下来手动适应边界原点啊。真相其实并不复杂,UIScrollView并不曾用CAScrollLayer,事实上,便是轻易的经过一向操作图层边界来促成滑动。

CAScrollLayer有二个机密的有用特色。假设你查看CAScrollLayer的头文件,你就会专注到有二个扩丰硕类完成了有个别措施和属性:

- (void)scrollPoint:(CGPoint)p;
- (void)scrollRectToVisible:(CGRect)r;
@property(readonly) CGRect visibleRect;

见到这个办法和属性名,你可能会感到那一个点子给各样CALayer实例扩充了滑动作效果能。可是事实上他们只是放置在CAScrollLayer中的图层的实用方法。scrollPoint:办法从图层树中找找并找到第三个可用的CAScrollLayer,然后滑动它使得钦定点改为可视的。scrollRectToVisible:办法达成了同样的业务只不过是成效在3个矩形上的。visibleRect质量决定图层(借使存在的话)的哪部分是近日的可视区域。固然您本人达成那么些办法就会相对轻易精晓某个,但是CAScrollLayer帮您省了这几个劳动,所以当提到到落成图层滑动的时候就足以用上了。

反射

运用CAReplicatorLayer并运用2个负比例转变于三个复制图层,你就足以创立钦定视图(或任何视图层次)内容的镜像图片,那样就创办了一个实时的『反射』效果。让大家来品尝完结这些创新意识:钦定3个再而三于UIView的ReflectionView,它会活动发出内容的反射功用。达成这些效应的代码很轻巧(见清单6.玖),实际上用ReflectionView完结这一个成效会更简明,大家只须要把ReflectionView的实例放置于Interface
Builder(见图六.玖),它就会实时生成子视图的反射,而不须要其他代码(见图6.拾).

清单陆.九 用CAReplicatorLayer自动绘制反射

#import "ReflectionView.h"
#import <QuartzCore/QuartzCore.h>

    @implementation ReflectionView

    + (Class)layerClass
    {
        return [CAReplicatorLayer class];
    }

    - (void)setUp
    {
        //configure replicator
        CAReplicatorLayer *layer = (CAReplicatorLayer *)self.layer;
        layer.instanceCount = 2;

        //move reflection instance below original and flip vertically
        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;

        //reduce alpha of reflection layer
        layer.instanceAlphaOffset = -0.6;
    }

    - (id)initWithFrame:(CGRect)frame
    {
        //this is called when view is created in code
        if ((self = [super initWithFrame:frame])) {
            [self setUp];
        }
        return self;
    }

    - (void)awakeFromNib
    {
        //this is called when view is created from a nib
        [self setUp];
    }
    @end

澳门葡京备用网址 20

6.9.png

澳门葡京备用网址 21

6.10.png

  • #### *CAScrollLayer

对于3个未改动的图层,它的bounds和它的frame是一样的,frame属性是由bounds属性自动总结而出的,所以改换任意二个值都会更新任何值。

可是壹旦您只想呈现多个大图层里面包车型地铁一小部分吗。比如说,你大概有一个相当的大的图形,你期望用户能够自由滑动,或然是3个数码或文本的长列表。在一个超人的iOS应用中,你恐怕会用到UITableView或是UIScrollView,然而对于单身的图层来讲,什么会等价于刚先生刚提到的UITableView和UIScrollView呢?

在第二章中,我们探寻了图层的contentsRect属性的用法,它确实是力所能及化解在图层中型小型地点显得大图片的缓解措施。然则只要您的图层包罗子图层那它就不是叁个相当好的消除方案,因为,这样做的话每一遍你想『滑动』可视区域的时候,你就要求手工业重新计算并更新具备的子图层地方。

其一时候就需求CAScrollLayer了。CAScrollLayer有2个-scrollToPoint:方法,它自动适应bounds的原点以便图层内容出现在滑行的地点。注意,那正是它做的享有业务。前边提到过,Core
Animation并不处理用户输入,所以CAScrollLayer并不担负将触摸事件转变为滑行事件,既不渲染滚动条,也不兑现别的iOS钦赐行为例如滑动反弹(当视图滑动超多了它的境界的将会反弹回正确的地点)。

让我们来用CAScrollLayer来常见3个主导的UIScrollView代替品。大家将会用CAScrollLayer作为视图的宿主图层,并创立四个自定义的UIView,然后用UIPanGestureRecognizer实现触摸事件响应。那段代码见清单陆.拾.
图陆.1一是运作效果:ScrollView显示了3个不止它的frame的UIImageView。

CATiledLayer

稍稍时候你可能要求绘制2个十分的大的图纸,常见的事例正是3个高像素的相片或然是地表的详细地图。iOS应用通畅运作在内部存款和储蓄器受限的装备上,所以读取整个图片到内部存款和储蓄器中是不明智的。载入大图大概会一定地慢,那个对您看上去对比有利的做法(在主线程调用UIImage-imageNamed:格局依然-imageWithContentsOfFile:办法)将会堵塞你的用户分界面,至少会挑起动画卡顿现象。

能快速绘制在iOS上的图片也有三个轻重限制。全体展现在显示器上的图纸最后都会被转载为OpenGL纹理,同时OpenGL有贰个最大的纹路尺寸(经常是204八*2048,或4096*40玖陆,这一个取决于设备型号)。固然您想在单个纹理中显得2个比那大的图,纵然图片已经存在于内部存款和储蓄器中了,你照旧会遇见相当大的属性难点,因为Core
Animation强制用CPU处理图片而不是更加快的GPU(见第3二章『速度的曲调』,和第33章『高效绘图』,它进一步详实地解说了软件绘图和硬件绘制)。

CATiledLayer为载入大图变成的习性难题提供了2个化解方案:将大图分解成小片然后将他们单独按需载入。让大家用试验来证实一下。

清单陆.十 用CAScrollLayer完成滑动视图

#import "ScrollView.h"
#import <QuartzCore/QuartzCore.h> @implementation ScrollView

    + (Class)layerClass
    {
        return [CAScrollLayer class];
    }

    - (void)setUp
    {
        //enable clipping
        self.layer.masksToBounds = YES;

        //attach pan gesture recognizer
        UIPanGestureRecognizer *recognizer = nil;
        recognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
        [self addGestureRecognizer:recognizer];
    }

    - (id)initWithFrame:(CGRect)frame
    {
        //this is called when view is created in code
        if ((self = [super initWithFrame:frame])) {
            [self setUp];
        }
        return self;
    }

    - (void)awakeFromNib {
        //this is called when view is created from a nib
        [self setUp];
    }

    - (void)pan:(UIPanGestureRecognizer *)recognizer
    {
        //get the offset by subtracting the pan gesture
        //translation from the current bounds origin
        CGPoint offset = self.bounds.origin;
        offset.x -= [recognizer translationInView:self].x;
        offset.y -= [recognizer translationInView:self].y;

        //scroll the layer
        [(CAScrollLayer *)self.layer scrollToPoint:offset];

        //reset the pan gesture translation
        [recognizer setTranslation:CGPointZero inView:self];
    }
    @end

不一样于UIScrollView,我们定制的滑行视图类并从未落实别的方式的边际检查(bounds
checking)。图层内容极有望滑出视图的分界并不过滑下去。CAScrollLayer并不曾一样UIScrollView中contentSize的习性,所以当CAScrollLayer滑动的时候完全没有四个大局的可滑动区域的定义,也无能为力自适应它的境界原点至你钦赐的值。它因而不能够自适应边界大小是因为它不须要,内容完全能够超越界限。

那您早晚会意外用CAScrollLayer的意思毕竟何在,因为你能够大约地用三个常见的CALayer然后手动适应边界原点啊。真相其实并不复杂,UIScrollView并不曾用CAScrollLayer,事实上,便是轻松的通过直接操作图层边界来兑现滑动。

CAScrollLayer有3个秘密的有用特色。借使您查看CAScrollLayer的头文件,你就会专注到有叁个扩丰富类达成了1部分措施和品质:

- (void)scrollPoint:(CGPoint)p;
- (void)scrollRectToVisible:(CGRect)r;
@property(readonly) CGRect visibleRect;

看来这个措施和属性名,你也许会以为这几个艺术给各样CALayer实例扩展了滑动作效果率。可是实际他们只是放置在CAScrollLayer中的图层的实用方法。scrollPoint:方法从图层树中搜索并找到第一个可用的CAScrollLayer,然后滑动它使得钦定点变为可视的。scrollRectToVisible:方法完毕了一如既往的事务只然而是法力在1个矩形上的。visibleRect属性决定图层(假设存在的话)的哪1部分是时下的可视区域。假诺您本人完毕那么些格局就会相对轻易理解有些,不过CAScrollLayer帮你省了那个劳动,所以当提到到贯彻图层滑动的时候就能够用上了。

  • #### CATiledLayer

多少时候你大概须要绘制四个相当的大的图纸,常见的例证正是二个高像素的肖像如故是地表的详尽地图。iOS应用通畅运作在内存受限的设施上,所以读取整个图片到内部存款和储蓄器中是不明智的。载入大图也许会一定地慢,那么些对你看起来相比较便利的做法(在主线程调用UIImage的-imageNamed:方法依然-imageWithContentsOfFile:方法)将会卡住你的用户分界面,至少会滋生动画卡顿现象。

能急忙绘制在iOS上的图片也有八个大小限制。全部显示在显示屏上的图纸最终都会被转正为OpenGL纹理,同时OpenGL有3个最大的纹路尺寸(平常是204八2048,或40964096,那一个取决于设备型号)。如若您想在单个纹理中显示三个比那大的图,即便图片已经存在于内部存款和储蓄器中了,你依然会遇见异常的大的属性难点,因为Core
Animation强制用CPU处理图片而不是更加快的GPU(见第3二章『速度的曲调』,和第叁三章『高效绘图』,它进一步详实地解说了软件绘图和硬件绘制)。

CATiledLayer为载入大图形成的性情难点提供了贰个消除方案:将大图分解成小片然后将他们单独按需载入。让我们用试验来证实一下。

小片裁剪

其一示例中,我们将会从三个2048*20伍拾壹分辨率的雪人图片动手。为了能够从CATiledLayer中收入,大家需求把这一个图形裁切成大多小部分的图样。你可以经过代码来成功那件业务,可是假设您在运作时读入整个图片并裁切,那CATiledLayer那么些全体的天性优点就损失殆尽了。理想状态下来讲,最CANON够各种步骤来促成。

清单六.11 演示了一个简约的Mac
OS命令行程序,它用CATiledLayer将三个图片裁剪成小图并蕴藏到分歧的公文中。

清单陆.1一 裁剪图片成小图的极限程序

#import <AppKit/AppKit.h>

int main(int argc, const char * argv[])
{
    @autoreleasepool{
        //handle incorrect arguments
        if (argc < 2) {
            NSLog(@"TileCutter arguments: inputfile");
            return 0;
        }

        //input file
        NSString *inputFile = [NSString stringWithCString:argv[1] encoding:NSUTF8StringEncoding];

        //tile size
        CGFloat tileSize = 256; //output path
        NSString *outputPath = [inputFile stringByDeletingPathExtension];

        //load image
        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];

        //calculate rows and columns
        NSInteger rows = ceil(size.height / tileSize);
        NSInteger cols = ceil(size.width / tileSize);

        //generate tiles
        for (int y = 0; y < rows; ++y) {
            for (int x = 0; x < cols; ++x) {
            //extract tile image
            CGRect tileRect = CGRectMake(x*tileSize, y*tileSize, tileSize, tileSize);
            CGImageRef tileImage = CGImageCreateWithImageInRect(imageRef, tileRect);

            //convert to jpeg data
            NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:tileImage];
            NSData *data = [imageRep representationUsingType: NSJPEGFileType properties:nil];
            CGImageRelease(tileImage);

            //save file
            NSString *path = [outputPath stringByAppendingFormat: @"_%02i_%02i.jpg", x, y];
            [data writeToFile:path atomically:NO];
            }
        }
    }
    return 0;
}

本条顺序将2048*20四十伍分辨率的雪人图案裁剪成了陆10个分裂的25陆*256的小图。(256*256是CATiledLayer的暗中同意小图大小,暗许大小能够经过tileSize品质改造)。程序接受1个图纸路径作为命令行的首先个参数。我们得以在编译的scheme将路线参数硬编码然后就可以在Xcode中运作了,可是随后效率在另二个图形上就不方便人民群众了。所以,大家编写翻译了那么些顺序并把它保存到敏感的地方,然后从巅峰调用,如上面所示:

> path/to/TileCutterApp path/to/Snowman.jpg

The app is very basic, but could easily be extended to support
additional arguments such as tile size, or to export images in formats
other than JPEG. The result of running it is a sequence of 64 new
images, named as follows:

其一顺序一定基础,不过可以自由地扩大支持额外的参数比如小图大小,大概导出格式等等。运营结果是617个新图的队列,如下边命名:

Snowman_00_00.jpg
Snowman_00_01.jpg
Snowman_00_02.jpg
...
Snowman_07_07.jpg

既是大家有了裁切后的小图,我们就要让iOS程序用到他们。CATiledLayer很好地和UIScrollView集成在一同。除了设置图层和滑动视图边界以适配整个图片大小,我们真的要做的正是落成-drawLayer:inContext:方式,当要求载入新的小图时,CATiledLayer就会调用到那么些主意。

清单陆.1二示范了代码。图六.1贰是代码运维结果。

清单陆.1二 三个简便的轮转CATiledLayer实现

#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIScrollView *scrollView;

@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //add the tiled layer
    CATiledLayer *tileLayer = [CATiledLayer layer];
    tileLayer.frame = CGRectMake(0, 0, 2048, 2048);
    tileLayer.delegate = self; [self.scrollView.layer addSublayer:tileLayer];

    //configure the scroll view
    self.scrollView.contentSize = tileLayer.frame.size;

    //draw layer
    [tileLayer setNeedsDisplay];
}

- (void)drawLayer:(CATiledLayer *)layer inContext:(CGContextRef)ctx
{
    //determine tile coordinate
    CGRect bounds = CGContextGetClipBoundingBox(ctx);
    NSInteger x = floor(bounds.origin.x / layer.tileSize.width);
    NSInteger y = floor(bounds.origin.y / layer.tileSize.height);

    //load tile image
    NSString *imageName = [NSString stringWithFormat: @"Snowman_%02i_%02i", x, y];
    NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"jpg"];
    UIImage *tileImage = [UIImage imageWithContentsOfFile:imagePath];

    //draw tile
    UIGraphicsPushContext(ctx);
    [tileImage drawInRect:bounds];
    UIGraphicsPopContext();
}
@end

澳门葡京备用网址 22

图 6.12

图6.12 用UIScrollView滚动CATiledLayer

当您滑动那些图片,你会发觉当CATiledLayer载入小图的时候,他们会淡入到分界面中。那是CATiledLayer的暗中认可行为。(你大概曾经在iOS
陆在此以前的苹果地图程序中见过这一个成效)你能够用fadeDuration属性更动淡入时间长度或直接禁止使用掉。CATiledLayer(差别于大多数的UIKit和Core
Animation方法)协理四线程绘制,-drawLayer:inContext:主意能够在多少个线程中并且地并发调用,所以请战战兢兢地保险您在那一个格局中落实的绘图代码是线程安全的。

小片裁剪

本条示例中,我们将会从三个204八*20四十六分辨率的雪人图片出手。为了能够从CATiledLayer中受益,大家要求把那几个图形裁切成大多小1些的图纸。你可以因而代码来完结那件工作,不过假使你在运营时读入整个图片并裁切,那CATiledLayer这个具备的天性优点就损失殆尽了。理想图景下来讲,最CANON够每一个步骤来促成。

清单陆.1一 演示了二个简练的Mac
OS命令行程序,它用CATiledLayer将1个图形裁剪成小图并储存到分裂的文本中。

Retina小图

您或者已经注意到了那一个小图并不是以Retina的分辨率展现的。为了以荧屏的原面生辨率来渲染CATiledLayer,大家要求安装图层的contentsScale来匹配UIScreenscale属性:

tileLayer.contentsScale = [UIScreen mainScreen].scale;

有意思的是,tileSize是以像素为单位,而不是点,所以增大了contentsScale就自行有了暗中认可的小图尺寸(以往它是12八*12八的点而不是25陆*256).所以,我们不须求手工更新小图的尺码恐怕在Retina分辨率下内定一个差异的小图。我们须求做的是适应小图渲染代码以对应安顿scale的变化,然而:

//determine tile coordinate
CGRect bounds = CGContextGetClipBoundingBox(ctx);
CGFloat scale = [UIScreen mainScreen].scale;
NSInteger x = floor(bounds.origin.x / layer.tileSize.width * scale);
NSInteger y = floor(bounds.origin.y / layer.tileSize.height * scale);

经过这几个措施校正scale也代表我们的雪人图将以六分之三的分寸渲染在Retina设备上(总尺寸是102四*1024,而不是2048*204八)。这一个普通都不会影响到用CATiledLayer好端端展现的图形类型(比如照片和地图,他们在统一筹划上便是要援助加大缩短,可以在不一样的缩放条件下显得),可是也亟需在心里知道。

清单陆.1一 裁剪图片成小图的巅峰程序
#import <AppKit/AppKit.h>

    int main(int argc, const char * argv[])
    {
        @autoreleasepool{
            //handle incorrect arguments
            if (argc < 2) {
                NSLog(@"TileCutter arguments: inputfile");
                return 0;
            }

            //input file
            NSString *inputFile = [NSString stringWithCString:argv[1] encoding:NSUTF8StringEncoding];

            //tile size
            CGFloat tileSize = 256; //output path
            NSString *outputPath = [inputFile stringByDeletingPathExtension];

            //load image
            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];

            //calculate rows and columns
            NSInteger rows = ceil(size.height / tileSize);
            NSInteger cols = ceil(size.width / tileSize);

            //generate tiles
            for (int y = 0; y < rows; ++y) {
                for (int x = 0; x < cols; ++x) {
                    //extract tile image
                    CGRect tileRect = CGRectMake(x*tileSize, y*tileSize, tileSize, tileSize);
                    CGImageRef tileImage = CGImageCreateWithImageInRect(imageRef, tileRect);

                    //convert to jpeg data
                    NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:tileImage];
                    NSData *data = [imageRep representationUsingType: NSJPEGFileType properties:nil];
                    CGImageRelease(tileImage);

                    //save file
                    NSString *path = [outputPath stringByAppendingFormat: @"_%02i_%02i.jpg", x, y];
                    [data writeToFile:path atomically:NO];
                }
            }
        }
        return 0;
    }

其1顺序将204八20五十几分辨率的雪人图案裁剪成了617个不等的25六256的小图。(256*256是CATiledLayer的私下认可小图大小,暗中认可大小能够由此tileSize属性改造)。程序接受2个图纸路径作为命令行的首先个参数。我们能够在编译的scheme将路线参数硬编码然后就足以在Xcode中运维了,不过之后成效在另1个图形上就不便于了。所以,大家编译了这几个程序并把它保存到敏感的地点,然后从巅峰调用,如上边所示:
> path/to/TileCutterApp path/to/Snowman.jpg

The app is very basic, but could easily be extended to support
additional arguments such as tile size, or to export images in formats
other than JPEG. The result of running it is a sequence of 64 new
images, named as follows:

其一程序一定基础,不过能够随意地扩张扶助额外的参数比如小图大小,或然导出格式等等。运行结果是陆十一个新图的队列,如上边命名:

Snowman_00_00.jpg
Snowman_00_01.jpg
Snowman_00_02.jpg
...
Snowman_07_07.jpg

既是大家有了裁切后的小图,大家将要让iOS程序用到他们。CATiledLayer很好地和UIScrollView集成在一齐。除了安装图层和滑动视图边界以适配整个图片大小,大家实在要做的便是兑现-drawLayer:inContext:方法,当需求载入新的小图时,CATiledLayer就会调用到这么些法子。

清单陆.1二演示了代码。图6.1二是代码运转结果。

CAEmitterLayer

在iOS
5中,苹果引进了三个新的CALayer子类叫做CAEmitterLayerCAEmitterLayer是二个高质量的粒子引擎,被用来创建实时例子动画如:上坡雾,火,雨等等那一个效应。

CAEmitterLayer看起来像是大多CAEmitterCell的容器,这些CAEmitierCell概念了三个例证效果。你将会为差异的事例效果定义二个或八个CAEmitterCell用作模版,同时CAEmitterLayer担当依据那一个模版实例化3个粒子流。一个CAEmitterCell好像于2个CALayer:它有一个contents属性可以定义为二个CGImage,别的还有一部分可安装属性决定着表现和表现。大家不会对那么些属性逐一进行详细的叙述,你们能够在CAEmitterCell类的头文件中找到。

我们来举个例证。大家将利用在一圆中发出不相同速度和折射率的粒子创制三个火热炸的职能。清单6.一三包罗了改换爆炸的代码。图陆.一叁是运营结果

清单6.13 用CAEmitterLayer创造爆炸效果

#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *containerView;

@end


@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    //create particle emitter layer
    CAEmitterLayer *emitter = [CAEmitterLayer layer];
    emitter.frame = self.containerView.bounds;
    [self.containerView.layer addSublayer:emitter];

    //configure emitter
    emitter.renderMode = kCAEmitterLayerAdditive;
    emitter.emitterPosition = CGPointMake(emitter.frame.size.width / 2.0, emitter.frame.size.height / 2.0);

    //create a particle template
    CAEmitterCell *cell = [[CAEmitterCell alloc] init];
    cell.contents = (__bridge id)[UIImage imageNamed:@"Spark.png"].CGImage;
    cell.birthRate = 150;
    cell.lifetime = 5.0;
    cell.color = [UIColor colorWithRed:1 green:0.5 blue:0.1 alpha:1.0].CGColor;
    cell.alphaSpeed = -0.4;
    cell.velocity = 50;
    cell.velocityRange = 50;
    cell.emissionRange = M_PI * 2.0;

    //add particle template to emitter
    emitter.emitterCells = @[cell];
}
@end

澳门葡京备用网址 23

图 6.13

图陆.一3 火焰爆炸效果

CAEMitterCell的性质基本上可以分成二种:

  • 那种粒子的某1品质的开始值。比如,color属性钦定了多个足以勾兑图片内姿色色的混合色。在演示中,大家将它设置为桔色。
  • 粒子某一属性的转移范围。比如emissionRange性能的值是贰π,那意味着粒子能够从360度自由地方反射出来。即使内定二个小片段的值,就足以成立出1个正方形。
  • 钦赐值在岁月线上的变通。比如,在演示中,我们将alphaSpeed安装为-0.四,正是说粒子的光滑度每过壹秒就是削减0.四,那样就有发出出来今后稳步消失的作用。

CAEmitterLayer的特性它和谐调控着漫天粒子系统的职位和形态。1些性情比如birthRatelifetimecelocity,那些属性在CAEmitterCell中也有。那个属性会以相乘的艺术效果在一块儿,这样您就足以用3个值来加快恐怕扩大整个粒子系统。别的值得一谈到的天性有以下那么些:

  • preservesDepth,是还是不是将3D粒子系统平面化到3个图层(默许值)只怕能够在3D空间中夹杂其余的图层。
  • renderMode,调节着在视觉上粒子图片是哪些混合的。你或许早已注意到了示范中大家把它设置为kCAEmitterLayerAdditive,它实现了如此二个职能:合并粒子重叠部分的亮度使得看上去更加亮。假使大家把它设置为默许的kCAEmitterLayerUnordered,效果就没那么窘迫了(见图6.1四)。

澳门葡京备用网址 24

图 6.14

图陆.14 禁止混色之后的火舌粒子

清单陆.1二 二个轻松易行的轮转CATiledLayer完成
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>

    @interface ViewController ()

    @property (nonatomic, weak) IBOutlet UIScrollView *scrollView;

    @end

    @implementation ViewController

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        //add the tiled layer
        CATiledLayer *tileLayer = [CATiledLayer layer];
        tileLayer.frame = CGRectMake(0, 0, 2048, 2048);
        tileLayer.delegate = self; [self.scrollView.layer addSublayer:tileLayer];

        //configure the scroll view
        self.scrollView.contentSize = tileLayer.frame.size;

        //draw layer
        [tileLayer setNeedsDisplay];
    }

    - (void)drawLayer:(CATiledLayer *)layer inContext:(CGContextRef)ctx
    {
        //determine tile coordinate
        CGRect bounds = CGContextGetClipBoundingBox(ctx);
        NSInteger x = floor(bounds.origin.x / layer.tileSize.width);
        NSInteger y = floor(bounds.origin.y / layer.tileSize.height);

        //load tile image
        NSString *imageName = [NSString stringWithFormat: @"Snowman_%02i_%02i", x, y];
        NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"jpg"];
        UIImage *tileImage = [UIImage imageWithContentsOfFile:imagePath];

        //draw tile
        UIGraphicsPushContext(ctx);
        [tileImage drawInRect:bounds];
        UIGraphicsPopContext();
    }
    @end

澳门葡京备用网址 25

6.12.png

当你滑动这么些图形,你会意识当CATiledLayer载入小图的时候,他们会淡入到界面中。那是CATiledLayer的默许行为。(你恐怕已经在iOS
六此前的苹果地图程序中见过那些功能)你能够用fadeDuration属性改造淡入时间长度或直接禁止使用掉。CATiledLayer(分歧于当先60%的UIKit和Core
Animation方法)扶助多线程绘制,-drawLayer:inContext:方法可以在多少个线程中并且地并发调用,所以请如履薄冰地保管您在那几个艺术中完成的绘图代码是线程安全的。

CAEAGLLayer

当iOS要处理高质量图形绘制,须要时正是OpenGL。应该说它应有是终极的专长,至少对于非游戏的采纳来讲是的。因为相比Core
Animation和UIkit框架,它不堪设想地复杂。

OpenGL提供了Core
Animation的功底,它是底层的C接口,直接和BlackBerry,华为平板的硬件通讯,极少地抽象出来的方法。OpenGL未有对象可能图层的延续概念。它只是简短地处理三角形。OpenGL中拥有东西都以3D空间中有颜色和纹理的三角。用起来格外复杂和强硬,可是用OpenGL绘制iOS用户分界面就要求过多众多的干活了。

为了能够以高质量使用Core
Animation,你要求推断你供给绘制哪一类内容(矢量图形,例子,文本,等等),但后选用合适的图层去变现那一个剧情,Core
Animation中唯有一部分档次的剧情是被中度优化的;所以假如您想绘制的东西并不能够找到专业的图层类,想要获得高品质就比较费事情了。

因为OpenGL根本不会对您的剧情开始展览假诺,它能够绘制得相当快。利用OpenGL,你能够绘制任何你了解须求的聚集音讯和形制逻辑的剧情。所以众多游戏都爱不释手用OpenGL(那个情况下,Core
Animation的限定就一览无遗了:它优化过的内容类型并不一定能满足须要),可是这么借助,方便的惊人抽象接口就没了。

在iOS
第55中学,苹果引进了四个新的框架叫做GL基特,它去掉了1些设置OpenGL的纷纷,提供了一个称为CLKViewUIView的子类,帮你处理超过2/四的安装和制图工作。前提是每一样各类的OpenGL绘图缓冲的最底层可配备项还是需求您用CAEAGLLayer完成,它是CALayer的三个子类,用来呈现任意的OpenGL图形。

大部气象下你都不需求手动设置CAEAGLLayer(假使用GLKView),过去的光景就不要再提了。特其余,我们将设置2个OpenGL
ES 2.0的上下文,它是当代的iOS设备的规范做法。

纵然不要求GLKit也得以完毕那全数,不过GLKit囊括了成百上千特出的干活,比如设置终点和局地着色器,那些都以类C语言叫做GLSL自包蕴在程序中,同时在运转时载入到图片硬件中。编写GLSL代码和设置EAGLayer从未有过什么样关联,所以大家将用GLKBaseEffect类将着色逻辑抽象出来。别的的事务,我们依然会有过去的格局。

在始发从前,你供给将GLKit和OpenGLES框架参加到你的档次中,然后就足以兑现清单陆.第11四中学的代码,里面是安装一个GAEAGLLayer的起码职业,它采纳了OpenGL
ES 二.0 的绘图上下文,并渲染了一个化险为夷三角(见图6.15).

清单6.14 用CAEAGLLayer绘制一个三角

#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#import <GLKit/GLKit.h>

@interface ViewController ()

@property (nonatomic, weak) IBOutlet UIView *glView;
@property (nonatomic, strong) EAGLContext *glContext;
@property (nonatomic, strong) CAEAGLLayer *glLayer;
@property (nonatomic, assign) GLuint framebuffer;
@property (nonatomic, assign) GLuint colorRenderbuffer;
@property (nonatomic, assign) GLint framebufferWidth;
@property (nonatomic, assign) GLint framebufferHeight;
@property (nonatomic, strong) GLKBaseEffect *effect;

@end

@implementation ViewController

- (void)setUpBuffers
{
    //set up frame buffer
    glGenFramebuffers(1, &_framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);

    //set up color render buffer
    glGenRenderbuffers(1, &_colorRenderbuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderbuffer);
    [self.glContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.glLayer];
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_framebufferWidth);
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_framebufferHeight);

    //check success
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        NSLog(@"Failed to make complete framebuffer object: %i", glCheckFramebufferStatus(GL_FRAMEBUFFER));
    }
}

- (void)tearDownBuffers
{
    if (_framebuffer) {
        //delete framebuffer
        glDeleteFramebuffers(1, &_framebuffer);
        _framebuffer = 0;
    }

    if (_colorRenderbuffer) {
        //delete color render buffer
        glDeleteRenderbuffers(1, &_colorRenderbuffer);
        _colorRenderbuffer = 0;
    }
}

- (void)drawFrame {
    //bind framebuffer & set viewport
    glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
    glViewport(0, 0, _framebufferWidth, _framebufferHeight);

    //bind shader program
    [self.effect prepareToDraw];

    //clear the screen
    glClear(GL_COLOR_BUFFER_BIT); glClearColor(0.0, 0.0, 0.0, 1.0);

    //set up vertices
    GLfloat vertices[] = {
        -0.5f, -0.5f, -1.0f, 0.0f, 0.5f, -1.0f, 0.5f, -0.5f, -1.0f,
    };

    //set up colors
    GLfloat colors[] = {
        0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
    };

    //draw triangle
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glEnableVertexAttribArray(GLKVertexAttribColor);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 0, vertices);
    glVertexAttribPointer(GLKVertexAttribColor,4, GL_FLOAT, GL_FALSE, 0, colors);
    glDrawArrays(GL_TRIANGLES, 0, 3);

    //present render buffer
    glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
    [self.glContext presentRenderbuffer:GL_RENDERBUFFER];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    //set up context
    self.glContext = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES2];
    [EAGLContext setCurrentContext:self.glContext];

    //set up layer
    self.glLayer = [CAEAGLLayer layer];
    self.glLayer.frame = self.glView.bounds;
    [self.glView.layer addSublayer:self.glLayer];
    self.glLayer.drawableProperties = @{kEAGLDrawablePropertyRetainedBacking:@NO, kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8};

    //set up base effect
    self.effect = [[GLKBaseEffect alloc] init];

    //set up buffers
    [self setUpBuffers];

    //draw frame
    [self drawFrame];
}

- (void)viewDidUnload
{
    [self tearDownBuffers];
    [super viewDidUnload];
}

- (void)dealloc
{
    [self tearDownBuffers];
    [EAGLContext setCurrentContext:nil];
}
@end

澳门葡京备用网址 26

图 6.15

图6.15 用OpenGL渲染的CAEAGLLayer图层

在三个真正的OpenGL应用中,大家兴许会用NSTimerCADisplayLink周期性地每分钟调用-drawRrame办法60遍,同时会将几何图形生成和制图分开以便不会每一趟都再一次生成三角形的终极(那样也得以让大家绘制其余的1部分东西而不是多少个三角形而已),可是上边那一个事例已经丰富演示了绘图原则了。

Retina小图

你可能已经注意到了那么些小图并不是以Retina的分辨率展现的。为了以荧屏的原生疏辨率来渲染CATiledLayer
,我们要求设置图层的contentsScale来相称UIScreen的scale
属性:
tileLayer.contentsScale = [UIScreen mainScreen].scale;

有趣的是,tileSize是以像素为单位,而不是点,所以增大了contentsScale就自动有了默许的小图尺寸(今后它是12八12捌的点而不是25625陆).所以,大家不要求手工业更新小图的尺寸或许在Retina分辨率下钦命多少个不及的小图。大家要求做的是适应小图渲染代码以对应安插scale的转移,但是:

//determine tile coordinate
CGRect bounds = CGContextGetClipBoundingBox(ctx);
CGFloat scale = [UIScreen mainScreen].scale;
NSInteger x = floor(bounds.origin.x / layer.tileSize.width * scale);
NSInteger y = floor(bounds.origin.y / layer.tileSize.height * scale);

经过那个法子考订scale也象征大家的雪人图将以二分之一的轻重渲染在Retina设备上(总尺寸是拾贰肆1024,而不是20482048)。那么些1般都不会影响到用CATiledLayer符合规律展现的图样类型(比如照片和地图,他们在安排上正是要帮忙加大缩短,能够在差别的缩放条件下显得),可是也亟需在心头知道。

  • #### CAEmitterLayer

在iOS
5中,苹果引入了一个新的CALayer子类叫做CAEmitterLayer。CAEmitterLayer是二个高品质的粒子引擎,被用来创建实时例子动画如:气团雾,火,雨等等这么些作用。

CAEmitterLayer看上去像是诸多CAEmitterCell的器皿,这一个CAEmitierCell定义了二个例子效果。你将会为不一样的例证效果定义四个或几个CAEmitterCell作为模版,同时CAEmitterLayer负责基于这么些模版实例化一个粒子流。贰个CAEmitterCell类似于3个CALayer:它有1个contents属性能够定义为一个CGImage,此外还有部分可安装属性决定着表现和表现。大家不会对那一个属性逐壹进行详细的叙述,你们能够在CAEmitterCell类的头文件中找到。

笔者们来举个例证。大家将运用在一圆中发出不相同速度和发光度的粒子成立八个热点炸的成效。清单6.一叁暗含了转移爆炸的代码。图陆.13是运维结果

AVPlayerLayer

最终三个图层类型是AVPlayerLayer。就算它不是Core
Animation框架的1有的(AV前缀看上去像),AVPlayerLayer是有其余框架(AVFoundation)提供的,它和Core
Animation紧凑地构成在1道,提供了一个CALayer子类来展示自定义的内容类型。

AVPlayerLayer是用来在iOS上播报录像的。他是高端接口例如MPMoivePlayer的尾巴部分完结,提供了体现录制的底层调控。AVPlayerLayer的选择一定简单:你能够用+playerLayerWithPlayer:主意成立3个业已绑定了摄像播放器的图层,大概您可以先创建多少个图层,然后用player品质绑定二个AVPlayer实例。

在大家开首此前,大家必要增添AVFoundation到大家的类型中。然后,清单陆.15开立了2个简便的摄像播放器,图六.1⑥是代码运行结果。

清单6.15 用AVPlayerLayer播音录像

#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#import <AVFoundation/AVFoundation.h>

@interface ViewController ()

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

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    //get video URL
    NSURL *URL = [[NSBundle mainBundle] URLForResource:@"Ship" withExtension:@"mp4"];

    //create player and player layer
    AVPlayer *player = [AVPlayer playerWithURL:URL];
    AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];

    //set player layer frame and attach it to our view
    playerLayer.frame = self.containerView.bounds;
    [self.containerView.layer addSublayer:playerLayer];

    //play the video
    [player play];
}
@end

澳门葡京备用网址 27

图 6.16

图6.16 用AVPlayerLayer图层播放摄像的截图

大家用代码创设了四个AVPlayerLayer,可是大家照例把它增加到了一个器皿视图中,而不是一贯在controller中的主视图上增多。那样事实上是为着能够使用自动布局限制使得图层在最中间;不然,1旦装备被旋转了小编们就要手动重新放置地点,因为Core
Animation并不帮衬自动大小和机关布局(见第一章『图层几何学』)。

当然,因为AVPlayerLayerCALayer的子类,它一而再了父类的装有本性。我们并不会受限于要在五个矩形中播放录像;清单陆.1陆演示了在3D,圆角,有色边框,蒙板,阴影等功用(见图6.1柒).

清单六.1陆 给摄像扩充转变,边框和圆角

- (void)viewDidLoad
{
    ...
    //set player layer frame and attach it to our view
    playerLayer.frame = self.containerView.bounds;
    [self.containerView.layer addSublayer:playerLayer];

    //transform layer
    CATransform3D transform = CATransform3DIdentity;
    transform.m34 = -1.0 / 500.0;
    transform = CATransform3DRotate(transform, M_PI_4, 1, 1, 0);
    playerLayer.transform = transform;

    //add rounded corners and border
    playerLayer.masksToBounds = YES;
    playerLayer.cornerRadius = 20.0;
    playerLayer.borderColor = [UIColor redColor].CGColor;
    playerLayer.borderWidth = 5.0;

    //play the video
    [player play];
}

澳门葡京备用网址 28

图6.17 3D视角下的边框和圆角`AVPlayerLayer`

清单陆.1叁 用CAEmitterLayer创制爆炸效果
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>

    @interface ViewController ()

    @property (nonatomic, weak) IBOutlet UIView *containerView;

    @end


    @implementation ViewController

    - (void)viewDidLoad
    {
        [super viewDidLoad];

        //create particle emitter layer
        CAEmitterLayer *emitter = [CAEmitterLayer layer];
        emitter.frame = self.containerView.bounds;
        [self.containerView.layer addSublayer:emitter];

        //configure emitter
        emitter.renderMode = kCAEmitterLayerAdditive;
        emitter.emitterPosition = CGPointMake(emitter.frame.size.width / 2.0, emitter.frame.size.height / 2.0);

        //create a particle template
        CAEmitterCell *cell = [[CAEmitterCell alloc] init];
        cell.contents = (__bridge id)[UIImage imageNamed:@"Spark.png"].CGImage;
        cell.birthRate = 150;
        cell.lifetime = 5.0;
        cell.color = [UIColor colorWithRed:1 green:0.5 blue:0.1 alpha:1.0].CGColor;
        cell.alphaSpeed = -0.4;
        cell.velocity = 50;
        cell.velocityRange = 50;
        cell.emissionRange = M_PI * 2.0;

        //add particle template to emitter
        emitter.emitterCells = @[cell];
    }
    @end

CAEMitterCell的性质基本上能够分为二种:

  • 那种粒子的某一属性的早先值。比如,color属性钦命了三个方可勾兑图片内姿容色的混合色。在示范中,大家将它设置为桔色。

  • 事例某1本性的变动范围。比如emissionRange属性的值是2π,那表示例子可以从360度即兴地方反射出来。若是钦赐一个小1些的值,就足以创制出三个圆柱形

  • 钦点值在时间线上的转移。比如,在演示中,大家将阿尔法Speed设置为-0.4,便是说例子的反射率每过一秒正是缩减0.四,那样就有发出出来今后稳步小时的效益。

CAEmitterLayer的性情它和谐调节着全套例子系统的岗位和形象。壹些质量比如birthRate,lifetime和celocity,那个属性在CAEmitterCell中也有。那些属性会以相乘的主意效果在协同,那样您就足以用二个值来加速恐怕扩充整个例子系统。别的值得一聊到的属性有以下那么些:

  • renderMode,调整着在视觉上粒子图片是什么样混合的。你恐怕早就注意到了演示中我们把它设置为kCAEmitterLayerAdditive,它实现了那样多少个功力:合并例子重叠部分的亮度使得看上去更加亮。固然我们把它设置为暗中同意的kCAEmitterLayerUnordered,效果就没那么难堪了(见图六.1四)

  • preservesDepth,是不是将3D例证系统平面化到叁个图层(暗中认可值)只怕能够在3D空间中夹杂其他的图层

澳门葡京备用网址 29

6.14.png

  • #### CAEAGLLayer

当iOS要处理高质量图形绘制,须要时正是OpenGL。应该说它应该是最后的绝艺,至少对于非游戏的采用来说是的。因为相比较Core
Animation和UIkit框架,它不堪设想地复杂。

OpenGL提供了Core
Animation的底蕴,它是底层的C接口,间接和一加,平板电脑的硬件通讯,极少地抽象出来的措施。OpenGL没有目的恐怕图层的存在延续概念。它只是简短地拍卖三角形。OpenGL中装有东西都以3D空间中有颜色和纹理的三角。用起来相当复杂和庞大,不过用OpenGL绘制iOS用户界面就供给广大浩大的办事了。

为了能够以高质量使用Core
Animation,你须求看清你必要绘制哪一种内容(矢量图形,例子,文本,等等),但后选取优良的图层去变现那些情节,Core
Animation中只有1部分连串的始末是被中度优化的;所以若是您想绘制的东西并不能够找到专业的图层类,想要得到高品质就比较费事情了。

因为OpenGL根本不会对您的内容实行假诺,它能够绘制得一定快。利用OpenGL,你能够绘制任何你明白须要的会见音信和形态逻辑的内容。所以广大游戏都欣赏用OpenGL(那个情况下,Core
Animation的限定就料定了:它优化过的始末类型并不一定能满足须要),但是那样借助,方便的莫斯中国科学技术大学学抽象接口就没了。

在iOS
5中,苹果引进了2个新的框架叫做GLKit,它去掉了有的安装OpenGL的错综复杂,提供了3个称作CLKView的UIView的子类,帮你处理抢先52%的设置和制图职业。前提是五花八门的OpenGL绘图缓冲的尾巴部分可安顿项还是须要您用CAEAGLLayer完结,它是CALayer的一个子类,用来呈现任意的OpenGL图形。

大繁多情景下您都不要求手动设置CAEAGLLayer(假使用GLKView),过去的光景就不要再提了。特别的,大家将安装多个OpenGL
ES 二.0的上下文,它是当代的iOS设备的科班做法。

就算不须要GLKit也足以形成这一体,不过GLKit囊括了广大附加的做事,比如设置终点和一些着色器,那个都是类C语言叫做GLSL自包涵在程序中,同时在运维时载入到图片硬件中。编写GLSL代码和设置EAGLayer未有何关联,所以我们将用GLKBaseEffect类将着色逻辑抽象出来。其余的事体,我们还是会有过去的不二等秘书籍。

在起来此前,你须要将GLKit和OpenGLES框架出席到您的体系中,然后就能够达成清单6.第11四中学的代码,里面是设置多少个GAEAGLLayer的至少工作,它采用了OpenGL
ES 2.0 的绘图上下文,并渲染了2个有色三角(见图6.一5).

总结

那壹章大家大致概述了部分专用图层以及用他们达成的1部分功用,大家只是精晓到这几个图层的肤浅,像CATiledLayerCAEMitterLayer这几个类能够独立写一章的。不过,重点是铭刻CALayer是用处相当大的,而且它并不曾为持有望的气象举办优化。为了博取Core
Animation最佳的习性,你须求为你的劳作选对正确的工具,希望您可见挖掘这几个分裂的CALayer子类的效益。
那1章大家经过CAEmitterLayerAVPlayerLayer类轻易地接触到了部分动画片,在其次章,大家将接二连三深切研究动画,就从隐式动画开端。

清单陆.14 用CAEAGLLayer绘制1个三角
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#import <GLKit/GLKit.h>

    @interface ViewController ()

    @property (nonatomic, weak) IBOutlet UIView *glView;
    @property (nonatomic, strong) EAGLContext *glContext;
    @property (nonatomic, strong) CAEAGLLayer *glLayer;
    @property (nonatomic, assign) GLuint framebuffer;
    @property (nonatomic, assign) GLuint colorRenderbuffer;
    @property (nonatomic, assign) GLint framebufferWidth;
    @property (nonatomic, assign) GLint framebufferHeight;
    @property (nonatomic, strong) GLKBaseEffect *effect;

    @end

    @implementation ViewController

    - (void)setUpBuffers
    {
        //set up frame buffer
        glGenFramebuffers(1, &_framebuffer);
        glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);

        //set up color render buffer
        glGenRenderbuffers(1, &_colorRenderbuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderbuffer);
        [self.glContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.glLayer];
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_framebufferWidth);
        glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_framebufferHeight);

        //check success
        if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
            NSLog(@"Failed to make complete framebuffer object: %i", glCheckFramebufferStatus(GL_FRAMEBUFFER));
        }
    }

    - (void)tearDownBuffers
    {
        if (_framebuffer) {
            //delete framebuffer
            glDeleteFramebuffers(1, &_framebuffer);
            _framebuffer = 0;
        }

        if (_colorRenderbuffer) {
            //delete color render buffer
            glDeleteRenderbuffers(1, &_colorRenderbuffer);
            _colorRenderbuffer = 0;
        }
    }

    - (void)drawFrame {
        //bind framebuffer & set viewport
        glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
        glViewport(0, 0, _framebufferWidth, _framebufferHeight);

        //bind shader program
        [self.effect prepareToDraw];

        //clear the screen
        glClear(GL_COLOR_BUFFER_BIT); glClearColor(0.0, 0.0, 0.0, 1.0);

        //set up vertices
        GLfloat vertices[] = {
            -0.5f, -0.5f, -1.0f, 0.0f, 0.5f, -1.0f, 0.5f, -0.5f, -1.0f,
        };

        //set up colors
        GLfloat colors[] = {
            0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
        };

        //draw triangle
        glEnableVertexAttribArray(GLKVertexAttribPosition);
        glEnableVertexAttribArray(GLKVertexAttribColor);
        glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, 0, vertices);
        glVertexAttribPointer(GLKVertexAttribColor,4, GL_FLOAT, GL_FALSE, 0, colors);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        //present render buffer
        glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer);
        [self.glContext presentRenderbuffer:GL_RENDERBUFFER];
    }

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        //set up context
        self.glContext = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES2];
        [EAGLContext setCurrentContext:self.glContext];

        //set up layer
        self.glLayer = [CAEAGLLayer layer];
        self.glLayer.frame = self.glView.bounds;
        [self.glView.layer addSublayer:self.glLayer];
        self.glLayer.drawableProperties = @{kEAGLDrawablePropertyRetainedBacking:@NO, kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8};

        //set up base effect
        self.effect = [[GLKBaseEffect alloc] init];

        //set up buffers
        [self setUpBuffers];

        //draw frame
        [self drawFrame];
    }

    - (void)viewDidUnload
    {
        [self tearDownBuffers];
        [super viewDidUnload];
    }

    - (void)dealloc
    {
        [self tearDownBuffers];
        [EAGLContext setCurrentContext:nil];
    }
@end

澳门葡京备用网址 30

6.15.png

在一个确实的OpenGL应用中,大家或许会用NSTimer或CADisplayLink周期性地每分钟调用-drawHummerH二rame方法57次,同时会将几何图形生成和制图分开以便不会每一趟都重新生成三角形的终点(那样也得以让我们绘制其余的壹对事物而不是五个三角形形而已),然而上边那几个例子已经丰盛演示了绘图原则了。

  • #### AVPlayerLayer

末尾1个图层类型是AVPlayerLayer。尽管它不是Core
Animation框架的1局地(AV前缀看上去像),AVPlayerLayer是有别的框架(AVFoundation)提供的,它和Core
Animation紧凑地整合在1块儿,提供了三个CALayer子类来浮现自定义的剧情类型。

AVPlayerLayer是用来在iOS上播放录制的。他是高端接口例如MPMoivePlayer的尾部达成,提供了显示录制的底层调控。AVPlayerLayer的应用卓殊轻松:你可以用+playerLayerWithPlayer:方法创造三个壹度绑定了摄像播放器的图层,或然你可以先创制叁个图层,然后用player属性绑定2个AVPlayer实例。

在大家开首以前,大家须求增添AVFoundation到大家的种类中。然后,清单陆.一伍创设了2个简易的录制播放器,图陆.16是代码运营结果。

小说摘录自:https://github.com/AttackOnDobby/iOS-Core-Animation-Advanced-Techniques

清单6.15 用AVPlayerLayer播放摄像
#import "ViewController.h"
#import <QuartzCore/QuartzCore.h>
#import <AVFoundation/AVFoundation.h>

    @interface ViewController ()

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

    @implementation ViewController

    - (void)viewDidLoad
    {
        [super viewDidLoad];
        //get video URL
        NSURL *URL = [[NSBundle mainBundle] URLForResource:@"Ship" withExtension:@"mp4"];

        //create player and player layer
        AVPlayer *player = [AVPlayer playerWithURL:URL];
        AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:player];

        //set player layer frame and attach it to our view
        playerLayer.frame = self.containerView.bounds;
        [self.containerView.layer addSublayer:playerLayer];

        //play the video
        [player play];
    }
    @end

澳门葡京备用网址 31

6.16.png

大家用代码创立了多个AVPlayerLayer,但是大家仍旧把它增多到了三个器皿视图中,而不是一向在controller中的主视图上增多。这样事实上是为着能够使用机动布局限制使得图层在最中间;不然,一旦装备被旋转了咱们将在手动重新放置地方,因为Core
Animation并不援救活动大小和自动布局(见第三章『图层几何学』)。

本来,因为AVPlayerLayer是CALayer的子类,它一而再了父类的持有性情。大家并不会受限于要在三个矩形中播放摄像;清单6.1陆示范了在3D,圆角,有色边框,蒙板,阴影等职能(见图陆.1七).

清单陆.1陆 给录制增添转变,边框和圆角
    - (void)viewDidLoad
    {
        ...
        //set player layer frame and attach it to our view
        playerLayer.frame = self.containerView.bounds;
        [self.containerView.layer addSublayer:playerLayer];

        //transform layer
        CATransform3D transform = CATransform3DIdentity;
        transform.m34 = -1.0 / 500.0;
        transform = CATransform3DRotate(transform, M_PI_4, 1, 1, 0);
        playerLayer.transform = transform;

        //add rounded corners and border
        playerLayer.masksToBounds = YES;
        playerLayer.cornerRadius = 20.0;
        playerLayer.borderColor = [UIColor redColor].CGColor;
        playerLayer.borderWidth = 5.0;

        //play the video
        [player play];
    }
总结

这一章我们大致概述了某些专用图层以及用他们完成的有的效果,大家只是精通到那一个图层的皮毛,像CATiledLayer和CAEMitterLayer这几个类可以独自写1章的。可是,重点是朝思暮想CALayer是用处异常的大的,而且它并从未为持有十分大也许的场景进行优化。为了赢得Core
Animation最佳的天性,你要求为你的劳作选对正确的工具,希望您可见挖掘这一个分歧的CALayer子类的效能。
那1章大家透过CAEmitterLayer和AVPlayerLayer类轻松地接触到了有个别卡通,在其次章,大家将接二连三深切钻研动画,就从隐式动画早先。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website