CoreAnimation之专用图层,专用图层

By admin in 4858.com on 2019年4月18日

CAScrollLayer

对此二个未改动的图层,它的bounds和它的frame是一律的,frame天性是由bounds性能自动总括而出的,所以改变任意3个值都会更新任何值。

只是只要你只想展示一个大图层里面包车型大巴一小部分啊。比如说,你大概有一个十分的大的图形,你希望用户能够轻松滑动,或然是七个数据或文本的长列表。在3个知识丰硕的iOS应用中,你也许会用到UITableView或是UIScrollView,不过对于单身的图层来讲,什么会等价于刚(Yu-Gang)刚提到的UITableViewUIScrollView呢?

在其次章中,大家追究了图层的contentsRect天性的用法,它真的是力所能及消除在图层中型小型地方显得大图片的缓解方法。不过假如您的图层包蕴子图层那它就不是三个非常好的化解方案,因为,那样做的话每一遍你想『滑动』可视区域的时候,你就需求手工业重新计算并更新拥有的子图层地方。

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

让大家来用CAScrollLayer来广泛一个为主的UIScrollView代替品。大家将会用CAScrollLayer作为视图的宿主图层,并制造1个自定义的UIView,然后用UIPanGestureRecognizer完毕触摸事件响应。那段代码见清单陆.十.
图陆.1一是运转效果:ScrollView体现了一个超乎它的frameUIImageView

清单6.10 用CAScrollLayer实现滑动视图

 

4858.com 14858.com 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)。图层内容极有极大可能率滑出视图的分界并然则滑下去。CAScrollLayer并从未同样UIScrollViewcontentSize的属性,所以当CAScrollLayer滑动的时候完全未有三个大局的可滑动区域的概念,也不或然自适应它的界限原点至你钦赐的值。它因而不可能自适应边界大小是因为它不须要,内容完全能够当先界限。

那您早晚会奇怪用CAScrollLayer的意义毕竟何在,因为你可以简轻巧单地用二个家常便饭的CALayer接下来手动适应边界原点啊。真相其实并不复杂,UIScrollView并未用CAScrollLayer,事实上,正是轻巧的通过直接操作图层边界来落实滑动。

CAScrollLayer有1个秘密的有用特色。借使您查看CAScrollLayer的头文件,你就会注意到有一个恢宏分类完结了1些办法和性质:

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

 

总的来看这个办法和属性名,你大概会以为那些点子给每种CALayer实例增添了滑动功用。然则其实他们只是放置在CAScrollLayer中的图层的实用方法。scrollPoint:方法从图层树中寻觅并找到第3个可用的CAScrollLayer,然后滑动它使得钦赐点变为可视的。scrollRectToVisible:方法完成了平等的业务只可是是效果在3个矩形上的。visibleRect属性决定图层(如若存在的话)的哪部分是近日的可视区域。倘使你协调达成这个办法就会相对轻易通晓有些,可是CAScrollLayer帮你省了这个劳顿,所以当提到到落实图层滑动的时候就能够用上了。

 

CAScrollLayer

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

唯独借使你只想突显三个大图层里面包车型大巴一小部分呢。比如说,你也许有2个非常的大的图片,你指望用户能够自由滑动,大概是三个数据或文本的长列表。在八个头名的iOS应用中,你大概会用到UITableView或是UIScrollView,不过对于单身的图层来讲,什么会等价于刚先生刚提到的UITableViewUIScrollView呢?

在其次章中,大家斟酌了图层的contentsRect天性的用法,它实在是能够消除在图层中型小型地点显得大图片的消除办法。可是假如你的图层包涵子图层那它就不是1个相当好的解决方案,因为,那样做的话每回你想『滑动』可视区域的时候,你就供给手工业重新计算并立异具备的子图层地方。

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

让大家来用CAScrollLayer来广大1个主导的UIScrollView替代品。大家将会用CAScrollLayer用作视图的宿主图层,并成立二个自定义的UIView,然后用UIPanGestureRecognizer贯彻触摸事件响应。那段代码见清单六.拾.
图六.1①是运作效果:ScrollView来得了二个大于它的frameUIImageView

清单6.10 用CAScrollLayer贯彻滑动视图

 

4858.com 34858.com 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并不曾同样UIScrollViewcontentSize的属性,所以当CAScrollLayer滑动的时候完全未有三个大局的可滑动区域的定义,也无能为力自适应它的边际原点至你钦点的值。它之所以不能够自适应边界大小是因为它不须求,内容完全能够抢先界限。

那你势必会意外用CAScrollLayer的含义终究何在,因为你能够省略地用三个1般性的CALayer然后手动适应边界原点啊。真相其实并不复杂,UIScrollView并从未用CAScrollLayer,事实上,就是轻巧的经过一向操作图层边界来促成滑动。

CAScrollLayer有三个隐衷的有用特色。假使你查看CAScrollLayer的头文件,你就会专注到有2个扩大分类完成了有个别方法和特性:

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类了,同时大家也询问到了1些可怜实用的绘图和卡通效果。可是Core
Animation图层不仅仅能成效于图片和颜料而已。本章就会学习别的的一些图层类,进一步扩展使用Core
Animation绘图的工夫。

复杂的公司都是专门化的
Catharine R. Stimpson

CAShapeLayer

在第5章『视觉效果』大家上学到了不使用图片的情景下用CGPath去组织任意形状的影子。如若大家能用一样的秘技开创一样形状的图层就好了。

CAShapeLayer是一个经过矢量图形而不是bitmap来绘制的图层子类。你钦命诸如颜色和线宽等特性,用CGPath来定义想要绘制的图样,最终CAShapeLayer就自动渲染出来了。当然,你也足以用Core
Graphics直接向原始的CALyer的始末中绘制1个渠道,相比较直下,使用CAShapeLayer有以下部分亮点:

  • 渲染快捷。CAShapeLayer行使了硬件加速,绘制同1图形会比用Core
    Graphics快繁多。
  • 快捷使用内部存款和储蓄器。七个CAShapeLayer不供给像一般CALayer如出一辙创造一个过夜图形,所以不管有多大,都不会攻克太多的内部存储器。
  • 不会被图层边界剪开掉。贰个CAShapeLayer可以在边界之外绘制。你的图层路线不会像在运用Core
    Graphics的平凡CALayer壹如既往被剪裁掉(如小编辈在其次章所见)。
  • 不会冒出像素化。当您给CAShapeLayer做3D改造时,它不像二个有寄宿图的平日图层一样变得像素化。

到近来甘休,大家曾经查究过CALayer类了,同时大家也询问到了有些至极实惠的绘图和卡通片效果。但是Core
Animation图层不仅仅能作用于图片和颜料而已。本章就会学习别的的壹部分图层类,进一步庞大使用Core
Animation绘图的本领

创办贰个CGPath

CAShapeLayer能够用来绘制全数可以透过CGPath来表示的形态。那么些形象不自然要关闭,图层路径也不确定要不可破,事实上你能够在二个图层上制图好几个区别的造型。你能够调控一些脾气比如lineWith(线宽,用点表示单位),lineCap(线条结尾的典范),和lineJoin(线条之间的结合点的榜样);不过在图层层面你唯有3回机遇设置那些属性。假如你想用差异颜色或风格来绘制八个造型,就只好为每一个造型准备一个图层了。

清单6.一的代码用1个CAShapeLayer渲染多个简练的火柴人。CAShapeLayer属性是CGPathRef类型,可是我们用UIBezierPath扶植类成立了图层路径,那样大家就绝不怀恋人工释放CGPath了。图6.一是代码运维的结果。即便还不是很完善,可是毕竟知道了大要对吧!

清单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

4858.com 5

图6.1

图6.1 用CAShapeLayer绘图二个简约的火柴人

  • #### CAShapeLayer

圆角

其次章里面涉及了CAShapeLayer为成立圆角视图提供了2个方法,就是CALayercornerRadius质量(译者注:其实是在第四章提到的)。尽管选拔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作为视图的宿主图层,而不是丰盛四个子视图(图层蒙板的详尽说明见第5章『视觉效果』)。

在第5章『视觉效果』大家学习到了不使用图片的情况下用CG帕特h去组织任意形状的黑影。如果大家能用一样的点子开创同样形状的图层就好了。
CAShapeLayer是贰个透过矢量图形而不是bitmap来绘制的图层子类。你钦点诸如颜色和线宽等属性,用CGPath来定义想要绘制的图样,最后CAShapeLayer就机关渲染出来了。当然,你也能够用Core
Graphics直接向原始的CALyer的剧情中绘制叁个路线,比较直下,使用CAShapeLayer有以下部分亮点:

CATextLayer

CoreAnimation之专用图层,专用图层。用户分界面是不可能从1个独自的图样里面营造的。三个规划精良的Logo可以很好地显示二个开关或控件的意向,然而你迟早都要索要1个不错的过时风格的文书标签。

只要您想在2个图层里面显示文字,完全可以依靠图层代理直接将字符串使用Core
Graphics写入图层的剧情(那就是UILabel的精彩)。假设通过寄宿于图层的视图,直接在图层上操作,那实在一定麻烦。你要为每2个呈现文字的图层创制3个能像图层代理同样干活的类,还要逻辑上判定哪些图层必要展示哪个字符串,更别提还要记下不一致的字体,颜色等一文山会海乱7捌糟的事物。

侥幸的是这几个都以不须求的,Core
Animation提供了五个CALayer的子类CATextLayer,它以图层的情势包含了UILabel少了一些全体的绘图天性,并且额外提供了一些新的性状。

同样,CATextLayer也要比UILabel渲染得快得多。很少有人通晓在iOS
陆及后边的版本,UILabel其实是经过Web基特来兑现绘制的,那样就招致了当有大多文字的时候就会有高大的天性压力。而CATextLayer行使了Core
text,并且渲染得这一个快。

让我们来品尝用CATextLayer来显示一些文字。清单6.二的代码实现了那一功用,结果如图六.二所示。

清单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

4858.com 6

图6.2

图6.2 用CATextLayer来显示三个纯文本标签

借使您精心看那几个文件,你会意识五个出人意料的地点:那一个文件有部分像素化了。这是因为并未以Retina的格局渲染,第贰章提到了那个contentScale属性,用来决定图层内容应当以什么样的分辨率来渲染。contentsScale并不爱惜显示屏的拉伸因素而连日暗中同意为1.0。倘使大家想以Retina的品质来显示文字,大家就得手动地设置CATextLayercontentsScale属性,如下:

textLayer.contentsScale = [UIScreen mainScreen].scale;

如此那般就消除了这些标题(如图六.叁)

4858.com 7

图 6.3

图6.3 设置contentsScale来同盟荧屏

CATextLayerfont属性不是一个UIFont项目,而是一个CFTypeRef花色。这样可以依照你的求实必要来决定字体属性应该是用CGFontRef项目只怕CTFontRef品种(Core
Text字体)。同时字体大小也是用fontSize属性单独设置的,因为CTFontRefCGFontRef并不像UIFont一样包罗点大小。这些事例会告知你怎么样将UIFont转换成CGFontRef

另外,CATextLayerstring属性并不是您想像的NSString类型,而是id项目。这样您既能够用NSString也足以用NSAttributedString来钦定文本了(注意,NSAttributedString并不是NSString的子类)。属性化字符串是iOS用来渲染字体风格的建制,它以一定的形式来调整钦赐范围内的字符串的原来新闻,比如字体,颜色,字重,斜体等。

  • 渲染快速。CAShapeLayer使用了硬件加快,绘制同1图形会比用Core
    Graphics快繁多。
  • 迅猛使用内部存款和储蓄器。一个CAShapeLayer不供给像普通CALayer同样成立一个住宿图形,所以无论是有多大,都不会占领太多的内部存款和储蓄器。
  • 不会被图层边界剪开掉。2个CAShapeLayer能够在分界之外绘制。你的图层路线不会像在利用Core
    Graphics的常见CALayer相同被剪炒掉(如笔者辈在其次章所见)。
  • 不会现出像素化。当您给CAShapeLayer做3D转移时,它不像1个有寄宿图的经常图层同样变得像素化。

富文本

iOS
6中,Apple给UILabel和别的UIKit文本视图增加了直接的属性化字符串的支撑,应该说那是三个很便宜的特点。不超过实际在从iOS叁.2从头CATextLayer就曾经帮衬属性化字符串了。那样的话,假设你想要辅助更低版本的iOS系统,CATextLayer确切是你向界面中增添富文本的好法子,而且也不用去跟复杂的Core
Text打交道,也省了用UIWebView的麻烦。

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

图6.4是代码运维结果(注意丰硕浅湖蓝的下划线文本)

清单6.三 用NSAttributedString实现二个富文本标签。

#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

4858.com 8

图 6.4

图陆.四 用CATextLayer实现二个富文本标签。

始建叁个CG帕特h
CAShapeLayer可以用来绘制全体能够由此CGPath来表示的模样。那些样子不必然要关掉,图层路线也不自然要不可破,事实上你能够在1个图层上绘制好些个少个例外的形态。你能够决定1些属性比如lineWith(线宽,用点表示单位),lineCap(线条结尾的指南),和lineJoin(线条之间的结合点的榜样);不过在图层层面你只有三回机遇设置那一个属性。假诺你想用差别颜色或风格来绘制多个造型,就不得不为每一个造型准备3个图层了。

行距和字距

有至关重要提一下的是,由于绘制的贯彻机制分裂(Core
Text和WebKit),用CATextLayer渲染和用UILabel渲染出的文本行距和字距也不是不尽同样的。

两岸的出入程度(由使用的书体和字符决定)总的来说挺小,不过只要您想正确的展现普通便签和CATextLayer就自然要牢记那或多或少。

清单6.一的代码用三个CAShapeLayer渲染3个大致的火柴人。CAShapeLayer属性是CGPathRef类型,但是大家用UIBezierPath扶助类创制了图层路线,那样大家就不要思虑人工释放CGPath了。图陆.1是代码运营的结果。就算还不是很完善,但是究竟知道了大要对吗!

UILabel的代替品

大家已经申明了CATextLayerUILabel享有更加好的性质表现,同时还有相当的布局选项并且在iOS
五上支撑富文本。可是与一般的竹签比较而言会愈发繁琐1些。如若大家真正在急需3个UILabel的可用代替品,最佳是力所能及在Interface
Builder上开创大家的竹签,而且尽量地像相似的视图同样健康职业。

咱俩应当承继UILabel,然后增加二个子图层CATextLayer比量齐观写呈现文本的艺术。可是如故会有由UILabel-drawRect:方式创设的空寄宿图。而且由于CALayer不支持自动缩放和机关布局,子视图并不是主动追踪视图边界的轻重,所以每一趟视图大小被改成,大家只能手动更新子图层的边际。

笔者们确实想要的是三个用CATextLayer作为宿主图层的UILabel子类,那样就足以随着视图自动调控大小而且也从没冗余的寄宿图啦。

就如大家在第三章『图层树』商量的均等,每二个UIView都以借宿在一个CALayer的以身作则上。那么些图层是由视图自动制造和管制的,那大家可以用别的图层类型代替他么?壹旦被创建,我们就不能够代替那些图层了。但是只要大家继续了UIView,那大家就足以重写+layerClass办法使得在开立的时候能回去1个不等的图层子类。UIView会在开头化的时候调用+layerClass方法,然后用它的归来类型来成立宿主图层。

清单6.四演示了二个UILabel子类LayerLabelCATextLayer绘制它的难点,而不是调用一般的UILabel应用的较慢的-drawRect:方法。LayerLabel以身作则既能够用代码实现,也足以在Interface
Builder兑现,只要把1般的标签拖入视图之中,然后设置它的类是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用作宿主图层的另一功利就是视图自动安装了contentsScale属性。

在那么些大约的例证中,大家只是达成了UILabel的一部分风格和布局属性,可是有个别再改良一下我们就足以创制3个帮助UILabel装有成效竟然越来越多效益的LayerLabel类(你能够在局地线上的开源项目中找到)。

固然你打算扶助iOS
陆及以上,基于CATextLayer的标签大概就有微微局限性。不过总得来讲,倘若想在app里面丰富利用CALayer子类,用+layerClass来创立基于差别图层的视图是三个大约可复用的办法。

清单陆.一 用CAShapeLayer绘制3个火柴人
#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事物的时候,若是可以协会独立成分就太方便了。比如说,你想创制一个儿女的上肢:你就需求分明哪1部分是男女的手腕,哪部分是男女的膀子,哪一部分是亲骨肉的肘,哪一部分是子女的膀子,哪部分是子女的双肩等等。

当然是同意单独地移动每个地方的哇。以肘为教导会移动前臂和手,而不是肩膀。Core
Animation图层很轻松就足以让你在二D条件下做出那样的层级连串下的更改,可是3D情状下就不太或许,因为全体的图层都把她的孩子都平面化到1个气象中(第五章『转变』有涉嫌)。

CATransformLayer化解了这些主题素材,CATransformLayer不相同于普通的CALayer,因为它不能够显得它自身的始末。只有当存在了2个能作用于子图层的改造它才真正存在。CATransformLayer并不平面化它的子图层,所以它能够用于组织三个层级的3D结构,比如小编的上肢示例。

用代码创造三个臂膀供给一定多的代码,所以作者就演示得更简圣元(Synutra)些吧:在第伍章的立方体示例,我们将由此旋转camara来缓解图层平面化难点而不是像立方体示例代码中用的sublayerTransform。那是三个分外正确的手艺,可是只能效率域单个对象上,尽管您的情景包括多个立方,那大家就不可能用这一个技艺单独旋转他们了。

那正是说,就让大家来试一试CATransformLayer呢,第二个难题就来了:在第4章,我们是用多少个视图来布局了小编们的立方体,而不是独立的图层。大家不可能在不打乱已有些视图层次的前提下在三个小编不是有寄宿图的图层中放置贰个寄宿图图层。我们得以创设多少个新的UIView子类寄宿在CATransformLayer(用+layerClass措施)之上。可是,为了简化案例,我们仅仅重建了3个单身的图层,而不是应用视图。那代表我们不能够像第六章相同在立方身体表面面展现按键和标签,但是我们今日也用不到这几个特点。

清单⑥.5便是代码。大家以大家在第伍章使用过的同①基本逻辑放置立方体。不过并不像从前那么直接将立方面加多到容器视图的宿主图层,大家将他们放置到一个CATransformLayer中开创三个单身的立方体对象,然后将七个那样的立方体放进容器中。大家随便地给立方面染色以将她们分别开来,这样就无须靠标签或许光亮来区分他们。图陆.五是运行结果。

清单6.5 用CATransformLayer装配3个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

4858.com 9

图 6.5

图陆.伍 同一视角下的俩不等转变的立方体

import “DrawingView.h”

CAGradientLayer

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

import <QuartzCore/QuartzCore.h>

基础渐变

咱俩将从一个简约的红变蓝的对角线渐变伊始(见清单6.陆).这一个渐变色彩放在二个数组中,并赋给colors品质。那些数组成员接受CGColorRef类型的值(并不是从NSObject派生而来),所以我们要用通过bridge转变以有限协理理编辑译符合规律。

CAGradientLayer也有startPointendPoint品质,他们调控了渐变的趋势。那多少个参数是以单位坐标系举办的定义,所以左上角坐标是{0,
0},右下角坐标是{1, 一}。代码运转结果如图陆.陆

清单6.陆 轻松的二种颜色的对角线渐变

@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

4858.com 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
```

4858.com 11

6.4.png

行距和字距

有要求提一下的是,由于绘制的落到实处机制差别(Core
Text和WebKit),用CATextLayer渲染和用UILabel渲染出的文件行距和字距也不是不尽同样的。

双方的距离程度(由运用的字体和字符决定)总的来讲挺小,可是假如你想正确的来得普通便签和CATextLayer就势要求牢记那或多或少

UILabel的代替品
小编们早已表明了CATextLayer比UILabel有着越来越好的性质表现,同时还有额外的布局选项并且在iOS
5上匡助富文本。可是与1般的竹签比较来说会特别繁琐壹些。假设大家真正在供给2个UILabel的可用替代品,最棒是力所能及在Interface
Builder上创办我们的竹签,而且尽量地像相似的视图同样健康工作。

咱俩理应承接UILabel,然后增添一个子图层CATextLayer同等对待写展现文本的艺术。可是依旧会有由UILabel的-drawRect:方法创制的空寄宿图。而且由于CALayer不协助自动缩放和自动布局,子视图并不是主动追踪视图边界的高低,所以每趟视图大小被转移,大家只好手动更新子图层的疆界。

大家的确想要的是一个用CATextLayer作为宿主图层的UILabel子类,那样就可以随着视图自动调整大小而且也从没冗余的寄宿图啦。

就如大家在率先章『图层树』探究的均等,每三个UIView都以过夜在八个CALayer的示范上。那一个图层是由视图自动创造和管理的,那我们得以用别的图层类型取而代之么?壹旦被创制,大家就无法代替这么些图层了。不过倘诺我们后续了UIView,那大家就能够重写+layerClass方法使得在创设的时候能回来二个不一的图层子类。UIView会在初阶化的时候调用+layerClass方法,然后用它的归来类型来创制宿主图层。

清单6.4演示了三个UILabel子类LayerLabel用CATextLayer绘制它的标题,而不是调用一般的UILabel使用的较慢的-drawRect:方法。LayerLabel示例既能够用代码达成,也能够在Interface
Builder达成,只要把普通的价签拖入视图之中,然后设置它的类是LayerLabel就足以了。

多种渐变

倘使你愿意,colors属性能够分包众多颜料,所以创立1个彩虹同样的多种渐变也是很轻便的。默许情状下,那个颜色在空间上均匀地被渲染,可是大家得以用locations属性来调控空间。locations性子是二个浮点数值的数组(以NSNumber卷入)。这个浮点数定义了colors品质中各类分歧颜色的任务,同样的,也是以单位坐标系举行标定。0.0代表着渐变的开首,一.0意味着着结束。

locations数组并不是吓唬须要的,不过尽管你给它赋值了就决然要有限支撑locations的数组大小和colors数组大小一定要一致,不然你将会收获一个空手的渐变。

清单6.七显得了一个依据清单陆.陆的对角线渐变的代码改造。今后改成了从红到黄最后到石榴红的渐变。locations数组钦赐了0.0,0.二5和0.五多少个数值,那样这多少个渐变就有点像挤在了左上角。(如图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);
}

4858.com 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作为宿主图层的另1功利便是视图自动安装了contentsScale属性。

在这一个大致的例证中,大家只是完毕了UILabel的1有个别风格和布局属性,但是某些再改革一下我们就足以成立一个帮衬UILabel全体功用竟然越多效益的LayerLabel类(你能够在局地线上的开源项目中找到)。

要是您打算帮忙iOS
6及以上,基于CATextLayer的价签只怕就有稍许局限性。可是总得来说,若是想在app里面丰盛利用CALayer子类,用+layerClass来成立基于不一样图层的视图是三个简练可复用的艺术。

  • #### CATransformLayer

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

当然是允许单独地移动每个地方的哇。以肘为率掌握活动前臂和手,而不是肩膀。Core
Animation图层很轻易就能够让你在二D条件下做出那样的层级类别下的退换,可是3D境况下就不太可能,因为有着的图层都把他的儿女都平面化到二个气象中(第四章『调换』有关系)。

CATransformLayer消除了那一个主题材料,CATransformLayer不一样于壹般性的CALayer,因为它不能够显得它自身的剧情。只有当存在了三个能成效域子图层的调换它才真正存在。CATransformLayer并不平面化它的子图层,所以它亦可用于组织二个层级的3D结构,比如自身的上肢示例。

用代码创制1个双手要求一定多的代码,所以本人就演示得更简约一些呢:在第四章的立方体示例,大家将透过旋转camara来缓解图层平面化难题而不是像立方体示例代码中用的sublayerTransform。那是多少个万分不易的技艺,不过只可以成效域单个对象上,假使您的场景蕴含多个立方,那大家就无法用那些手艺单独旋转他们了。

那就是说,就让我们来试1试CATransformLayer吧,第5个难点就来了:在第陆章,大家是用多个视图来布局了小编们的立方体,而不是独立的图层。大家不能够在不打乱已有个别视图层次的前提下在二个自个儿不是有寄宿图的图层中放置3个寄宿图图层。大家得以创立多个新的UIView子类寄宿在CATransformLayer(用+layerClass方法)之上。不过,为了简化案例,大家1味重建了3个单身的图层,而不是行使视图。那意味着大家无法像第5章一样在立方体表面展现按键和标签,可是我们今后也用不到那么些特点。

清单六.5就是代码。我们以大家在第5章使用过的均等基本逻辑放置立方体。可是并不像从前那么直接将立方面增添到容器视图的宿主图层,我们将他们放置到3个CATransformLayer中开创2个独门的立方体对象,然后将七个那样的立方体放进容器中。大家随便地给立方面染色以将她们分别开来,那样就无须靠标签或许光亮来区分他们。图陆.5是运转结果。

CAReplicatorLayer

CAReplicatorLayer的指标是为着急迅转变好些个相似的图层。它会绘制3个或多少个图层的子图层,并在各类复制体上运用分歧的调换。看上去演示能够越来越解释那些,我们来写个例证吗。

清单6.伍 用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

    - (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

4858.com 13

6.5.png

  • #### *CAGradientLayer

CAGradientLayer是用来生成二种或越来越多颜色平滑渐变的。用Core
Graphics复制四个CAGradientLayer并将内容绘制到一个日常图层的寄宿图也是有异常的大概率的,但是CAGradientLayer的实在好处在于绘制使用了硬件加速。

基础渐变

大家将从三个轻松易行的红变蓝的对角线渐变初阶(见清单陆.6).这个渐变色彩放在3个数组中,并赋给colors属性。这些数组成员接受CGColorRef类型的值(并不是从NSObject派生而来),所以我们要用通过bridge转变以有限帮助理编辑译寻常。

CAGradientLayer也有startPoint和endPoint属性,他们操纵了渐变的动向。那八个参数是以单位坐标系进行的概念,所以左上角坐标是{0,
0},右下角坐标是{一, 1}。代码运维结果如图六.六

再也图层(Repeating Layers)

清单六.第88中学,我们在显示器的中级创造了三个小石黄方块图层,然后用CAReplicatorLayer转变13个图层组成二个圆形。instanceCount属性钦命了图层须要再行多少次。instanceTransform点名了2个CATransform3D3D转变(那种情状下,下一图层的活动和旋转将会移动到圆圈的下二个点)。

改造是逐年加多的,各类实例都以相对于前一实例布局。这就是为何这几个复制体最后不会冒出在允许地点上,图6.八是代码运维结果。

清单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

4858.com 14

图 6.8

图6.8 用CAReplicatorLayer始建壹圈图层

专注到当图层在重复的时候,他们的颜料也在扭转:那是用instanceBlueOffsetinstanceGreenOffset特性达成的。通过逐级减少蓝灰和深青莲通道,我们稳步将图层颜色转变来了革命。那么些复制效果看起来很酷,可是CAReplicatorLayer真正使用到实在程序上的地方比如:3个戏耍中程导弹弹的轨迹云,可能粒子爆炸(纵然iOS
伍已经引进了CAEmitterLayer,它更符合成立任意的粒子效果)。除此而外,还有三个实在使用是:反射。

清单6.陆 轻松的二种颜色的对角线渐变
 @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);
}
```

4858.com 15

6.7.png

  • #### CAReplicatorLayer

CAReplicatorLayer的目标是为了连忙转换许多貌似的图层。它会绘制一个或四个图层的子图层,并在每一种复制体上行使不一样的改换。看上去演示能够越来越解释那几个,我们来写个例证吗。

反射

使用CAReplicatorLayer并动用贰个负比例转变于多少个复制图层,你就足以成立钦命视图(或任何视图层次)内容的镜像图片,这样就创办了一个实时的『反射』效果。让我们来尝试实现那些创新意识:钦定二个三番五次于UIViewReflectionView,它会自动发出内容的反射成效。实现那些功用的代码很简短(见清单6.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

4858.com 16

图 6.9

图6.9 在Interface Builder中使用ReflectionView

4858.com 17

图 6.10

图6.10 ReflectionView电动实时产生反射功能。

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

双重图层(Repeating Layers)

清单6.第88中学,我们在显示器的中游成立了八个小深深湖蓝方块图层,然后用CAReplicatorLayer生成1一个图层组成一个圆形。instanceCount属性内定了图层须要再行多少次。instanceTransform钦点了二个CATransform3D3D改换(这种情景下,下1图层的移动和旋转将会活动到圆圈的下3个点)。

调换是稳步增添的,每一种实例都以周旋于前1实例布局。那正是为啥这么些复制体最后不会并发在同意地点上,图6.八是代码运维结果。

清单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

4858.com 18

6.8.png

专注到当图层在重复的时候,他们的颜色也在调换:那是用instanceBlueOffset和instance格林Offset属性完毕的。通过慢慢缩减深橙和肉色通道,我们稳步将图层颜色转换到了丁卯革命。这些复制效果看起来很酷,可是CAReplicatorLayer真正应用到骨子里程序上的场景比如:一个娱乐中程导弹弹的轨道云,或许粒子爆炸(即便iOS
5已经引入了CAEmitterLayer,它更符合创立任意的粒子效果)。除此而外,还有1个实在利用是:反射。

CAScrollLayer

对于二个未改动的图层,它的bounds和它的frame是1模同样的,frame属性是由bounds特性自动计算而出的,所以更换任意二个值都会更新任何值。

只是如若你只想体现1个大图层里面包车型大巴一小部分啊。比如说,你可能有贰个非常大的图形,你希望用户能够随意滑动,恐怕是一个数额或文本的长列表。在二个头名的iOS应用中,你或然会用到UITableView或是UIScrollView,不过对于单身的图层来说,什么会等价于刚同志刚提到的UITableViewUIScrollView呢?

在其次章中,大家追究了图层的contentsRect属性的用法,它的确是能够消除在图层中型小型地点显得大图片的缓解格局。不过假使你的图层蕴涵子图层那它就不是三个至极好的消除方案,因为,那样做的话每一趟你想『滑动』可视区域的时候,你就要求手工业重新总结并创新具备的子图层地点。

以此时候就必要CAScrollLayer了。CAScrollLayer有一个-scrollToPoint:主意,它自动适应bounds的原点以便图层内容出现在滑行的地点。注意,这就是它做的保有业务。后面提到过,Core
Animation并不处理用户输入,所以CAScrollLayer并不负担将触摸事件调换为滑行事件,既不渲染滚动条,也不兑现其余iOS钦定行为例如滑动反弹(当视图滑动超多了它的边界的将会反弹回正确的地方)。

让大家来用CAScrollLayer来普及一个着力的UIScrollView替代品。我们将会用CAScrollLayer用作视图的宿主图层,并创办四个自定义的UIView,然后用UIPanGestureRecognizer贯彻触摸事件响应。那段代码见清单陆.十.
图6.11是运作效果: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

4858.com 19

图 6.11

图6.11 用UIScrollView开创多少个成团的滑行视图

不同于UIScrollView,大家定制的滑动视图类并未兑现任何款式的边际检查(bounds
checking)。图层内容极有希望滑出视图的界线并不过滑下去。CAScrollLayer并从未一样UIScrollViewcontentSize的属性,所以当CAScrollLayer滑动的时候完全未有多少个大局的可滑动区域的定义,也无能为力自适应它的界限原点至你钦命的值。它之所以不能自适应边界大小是因为它不须求,内容完全能够超越界限。

那你一定会意外用CAScrollLayer的意义毕竟何在,因为您能够省略地用二个常备的CALayer接下来手动适应边界原点啊。真相其实并不复杂,UIScrollView并未用CAScrollLayer,事实上,正是轻松的经过一向操作图层边界来促成滑动。

CAScrollLayer有二个秘密的有用特色。要是您查看CAScrollLayer的头文件,你就会小心到有二个扩大分类完结了有个别艺术和属性:

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

总的来看这几个方法和属性名,你大概会认为那一个办法给各种CALayer实例增添了滑动成效。可是事实上他们只是放置在CAScrollLayer中的图层的实用方法。scrollPoint:措施从图层树中找找并找到第八个可用的CAScrollLayer,然后滑动它使得钦定点变为可视的。scrollRectToVisible:措施达成了同样的作业只可是是效果在三个矩形上的。visibleRect属性决定图层(假诺存在的话)的哪一部分是现阶段的可视区域。即便您本身完成这个方法就会相对轻巧领会有些,不过CAScrollLayer帮你省了这么些辛劳,所以当提到到落到实处图层滑动的时候就足以用上了。

反射

使用CAReplicatorLayer并使用3个负比例转换于一个复制图层,你就足以创立内定视图(或任何视图层次)内容的镜像图片,这样就创设了贰个实时的『反射』效果。让大家来品尝完成这么些创新意识:钦点3个承接于UIView的ReflectionView,它会自动发出内容的反射成效。完毕那个效应的代码不会细小略(见清单陆.玖),实际上用ReflectionView实现这么些意义会更轻松,大家只须求把ReflectionView的实例放置于Interface
Builder(见图陆.玖),它就会实时生成子视图的反射,而不须求其余代码(见图六.10).

清单陆.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

4858.com 20

6.9.png

4858.com 21

6.10.png

  • #### *CAScrollLayer

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

不过只要您只想显示3个大图层里面包车型客车一小部分吗。比如说,你也许有三个相当的大的图样,你期望用户可以自由滑动,恐怕是3个多少或文本的长列表。在一个超人的iOS应用中,你可能会用到UITableView或是UIScrollView,可是对于单身的图层来说,什么会等价Yu Gang刚提到的UITableView和UIScrollView呢?

在第三章中,我们追究了图层的contentsRect属性的用法,它确实是力所能及化解在图层中小地点显得大图片的解决措施。不过假诺您的图层包括子图层那它就不是1个格外好的化解方案,因为,这样做的话每一遍你想『滑动』可视区域的时候,你就须求手工业重新总结并革新具有的子图层地方。

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

让大家来用CAScrollLayer来常见叁个主导的UIScrollView替代品。我们将会用CAScrollLayer作为视图的宿主图层,并创办二个自定义的UIView,然后用UIPanGestureRecognizer完成触摸事件响应。那段代码见清单6.十.
图陆.1一是运营效果:ScrollView显示了一个大于它的frame的UIImageView。

CATiledLayer

稍微时候你大概必要绘制一个非常的大的图样,常见的例子便是三个高像素的肖像或然是地表的详实地图。iOS应用通畅运作在内部存款和储蓄器受限的设备上,所以读取整个图片到内部存款和储蓄器中是不明智的。载入大图或者会一定地慢,那几个对您看上去相比较便宜的做法(在主线程调用UIImage-imageNamed:措施依旧-imageWithContentsOfFile:格局)将会阻塞你的用户分界面,至少会引起动画卡顿现象。

能非常的慢绘制在iOS上的图样也有2个大小限制。全部呈现在荧屏上的图片最后都会被转正为OpenGL纹理,同时OpenGL有2个最大的纹理尺寸(日常是204八*2048,或4096*40玖陆,那个取决于设备型号)。倘诺您想在单个纹理中展现一个比那大的图,即使图片已经存在于内部存款和储蓄器中了,你依旧会遇到十分的大的习性难点,因为Core
Animation强制用CPU处理图片而不是更加快的GPU(见第一二章『速度的曲调』,和第二三章『高效绘图』,它特别详细地演讲了软件绘图和硬件绘制)。

CATiledLayer为载入大图形成的性质难题提供了叁个化解方案:将大图分解成小片然后将她们独立按需载入。让咱们用试验来证雅培下。

清单陆.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

不一样于UIScrollView,大家定制的滑行视图类并从未落到实处其余情势的界限检查(bounds
checking)。图层内容极有十分大恐怕滑出视图的境界并可是滑下去。CAScrollLayer并不曾一样UIScrollView中contentSize的品质,所以当CAScrollLayer滑动的时候完全未有贰个大局的可滑动区域的定义,也心慌意乱自适应它的界线原点至你内定的值。它由此不能够自适应边界大小是因为它不须求,内容完全能够当先界限。

那你势必会意外用CAScrollLayer的含义究竟何在,因为您可以大致地用贰个普通的CALayer然后手动适应边界原点啊。真相其实并不复杂,UIScrollView并从未用CAScrollLayer,事实上,便是轻巧的通过平昔操作图层边界来兑现滑动。

CAScrollLayer有二个地下的有用特色。若是你查看CAScrollLayer的头文件,你就会注意到有3个恢弘分类完结了壹部分艺术和质量:

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

来看那一个办法和属性名,你或然会以为那么些点子给各类CALayer实例扩张了滑动功效。然而实际上他们只是放置在CAScrollLayer中的图层的实用方法。scrollPoint:方法从图层树中寻觅并找到第二个可用的CAScrollLayer,然后滑动它使得指确定地点改为可视的。scrollRectToVisible:方法完成了同壹的工作只可是是效果在贰个矩形上的。visibleRect属性决定图层(假若存在的话)的哪部分是当前的可视区域。假设您本身完成这么些措施就会相对轻松明白有个别,可是CAScrollLayer帮你省了那个劳碌,所以当提到到完毕图层滑动的时候就可以用上了。

  • #### CATiledLayer

稍许时候你只怕供给绘制三个异常的大的图片,常见的例子就是1个高像素的肖像依然是地表包车型客车事无巨细地图。iOS应用通畅运营在内部存款和储蓄器受限的设备上,所以读取整个图片到内部存款和储蓄器中是不明智的。载入大图只怕会一定地慢,那几个对你看起来相比便宜的做法(在主线程调用UIImage的-imageNamed:方法或然-imageWithContentsOfFile:方法)将会堵塞你的用户界面,至少会引起动画卡顿现象。

能神速绘制在iOS上的图形也有3个轻重限制。全部彰显在显示屏上的图样最终都会被转化为OpenGL纹理,同时OpenGL有二个最大的纹路尺寸(平日是204八2048,或409640玖陆,那个取决于设备型号)。纵然你想在单个纹理中显得七个比那大的图,纵然图片已经存在于内部存款和储蓄器中了,你照样会凌驾相当的大的性质难点,因为Core
Animation强制用CPU处理图片而不是更加快的GPU(见第三2章『速度的曲调』,和第二三章『高效绘图』,它越是详实地解释了软件绘图和硬件绘制)。

CATiledLayer为载入大图变成的品质难题提供了三个消除方案:将大图分解成小片然后将她们独立按需载入。让大家用试验来证惠氏(WYETH)(Dumex)下。

小片裁剪

其1示例中,大家将会从贰个204捌*20四十九分辨率的雪人图片入手。为了能够从CATiledLayer中低收入,大家须求把这几个图形裁切成繁多小部分的图样。你能够经过代码来产生那件事情,然则若是您在运营时读入整个图片并裁切,那CATiledLayer这一个有着的性质优点就损失殆尽了。理想状态下来说,最棒能够每一种步骤来贯彻。

清单六.1壹 演示了二个轻易易行的Mac
OS命令行程序,它用CATiledLayer将2个图纸裁剪成小图并蕴藏到不一致的公文中。

清单6.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;
}

那一个程序将204八*20四20分辨率的雪人图案裁剪成了6拾三个不等的25陆*256的小图。(256*256是CATiledLayer的暗中认可小图大小,暗中同意大小可以透过tileSize本性改造)。程序接受二个图片路线作为命令行的第3个参数。大家能够在编写翻译的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:

其一顺序一定基础,但是能够随意地扩充帮忙额外的参数比如小图大小,或许导出格式等等。运营结果是陆14个新图的行列,如下边命名:

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二是代码运转结果。

清单6.1二 3个简便的滚动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

4858.com 22

图 6.12

图6.12 用UIScrollView滚动CATiledLayer

当你滑动那么些图形,你会发觉当CATiledLayer载入小图的时候,他们会淡入到分界面中。那是CATiledLayer的暗中认可行为。(你也许曾经在iOS
陆从前的苹果地图程序中见过那么些效果)你能够用fadeDuration天性改换淡入时间长度或直接禁用掉。CATiledLayer(差异于半数以上的UIKit和Core
Animation方法)支持二1010二线程绘制,-drawLayer:inContext:格局能够在多个线程中并且地并发调用,所以请不敢越雷池一步地保险您在这几个方法中落到实处的绘图代码是线程安全的。

小片裁剪

那么些示例中,大家将会从三个2048*204七分辨率的雪人图片入手。为了能够从CATiledLayer中收入,咱们必要把那些图片裁切成繁多小部分的图样。你可以透过代码来形成那件事情,可是假设你在运行时读入整个图片并裁切,那CATiledLayer这个富有的属性优点就损失殆尽了。理想图景下来讲,最佳能(CANON)够每一种步骤来完结。

清单6.1一 演示了贰个粗略的Mac
OS命令行程序,它用CATiledLayer将3个图片裁剪成小图并储存到区别的公文中。

Retina小图

你大概已经注意到了那一个小图并不是以Retina的分辨率展现的。为了以显示屏的原生疏辨率来渲染CATiledLayer,大家须要安装图层的contentsScale来匹配UIScreenscale属性:

tileLayer.contentsScale = [UIScreen mainScreen].scale;

有趣的是,tileSize是以像素为单位,而不是点,所以增大了contentsScale就自动有了暗中同意的小图尺寸(现在它是12八*12捌的点而不是25六*256).所以,大家不需求手工更新小图的尺寸也许在Retina分辨率下钦定1个分歧的小图。大家供给做的是适应小图渲染代码以对应配备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也代表大家的雪人图将以6分之三的轻重渲染在Retina设备上(总尺寸是10二4*1024,而不是2048*2048)。这一个1般都不会潜移默化到用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;
    }

这一个程序将204八20四十陆分辨率的雪人图案裁剪成了陆拾三个不等的25陆256的小图。(256*256是CATiledLayer的暗中同意小图大小,私下认可大小能够透过tileSize属性改造)。程序接受1个图纸路线作为命令行的首先个参数。大家得以在编写翻译的scheme将路线参数硬编码然后就可以在Xcode中运作了,可是随后成效在另2个图纸上就不方便人民群众了。所以,大家编写翻译了这些顺序并把它保存到敏感的地方,然后从巅峰调用,如上边所示:
> 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:

其1顺序一定基础,但是能够轻松地扩张帮忙额外的参数比如小图大小,可能导出格式等等。运营结果是陆十一个新图的队列,如下边命名:

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

既是我们有了裁切后的小图,大家将在让iOS程序用到他俩。CATiledLayer很好地和UIScrollView集成在1道。除了安装图层和滑动视图边界以适配整个图片大小,我们真正要做的就是促成-drawLayer:inContext:方法,当供给载入新的小图时,CATiledLayer就会调用到那几个点子。

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

CAEmitterLayer

在iOS
5中,苹果引进了一个新的CALayer子类叫做CAEmitterLayerCAEmitterLayer是四个高性能的粒子引擎,被用来创建实时例子动画如:蒸发雾,火,雨等等那么些效应。

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

作者们来举个例证。大家将动用在1圆中发出分裂速度和折射率的粒子成立2个火热炸的效益。清单陆.一三包涵了变化爆炸的代码。图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

4858.com 23

图 6.13

图陆.一三 火焰爆炸效果

CAEMitterCell的质量基本上能够分成三种:

  • 那种粒子的某一质量的开头值。比如,color属性钦定了1个足以勾兑图片内姿容色的混合色。在演示中,大家将它设置为桔色。
  • 粒子某一属性的浮动范围。比如emissionRange品质的值是2π,那代表粒子能够从360度Infiniti制地点反射出来。倘若钦命1个小片段的值,就能够创设出3个星型。
  • 钦命值在岁月线上的变化。比如,在示范中,大家将alphaSpeed设置为-0.四,正是说粒子的发光度每过一秒正是削减0.4,那样就有发出出来现在稳步消散的意义。

CAEmitterLayer的天性它和谐主宰着全部粒子系统的职位和形象。1些个性比如birthRatelifetimecelocity,这一个属性在CAEmitterCell中也有。那么些属性会以相乘的主意效果在联合签字,那样您就足以用3个值来加快也许增加整个粒子系统。其余值得一提到的习性有以下这一个:

  • preservesDepth,是或不是将3D粒子系统平面化到二个图层(暗中认可值)也许能够在3D空间中混合其余的图层。
  • renderMode,调控着在视觉上粒子图片是怎么混合的。你大概早已注意到了演示中我们把它设置为kCAEmitterLayerAdditive,它完结了如此一个意义:合并粒子重叠部分的亮度使得看上去更加亮。若是大家把它设置为暗许的kCAEmitterLayerUnordered,效果就没那么狼狈了(见图六.1四)。

4858.com 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

4858.com 25

6.12.png

当你滑动那么些图片,你会意识当CATiledLayer载入小图的时候,他们会淡入到分界面中。那是CATiledLayer的暗许行为。(你可能曾经在iOS
6在此之前的苹果地图程序中见过这些功效)你能够用fadeDuration属性别变化更淡入时间长度或直接禁止使用掉。CATiledLayer(分裂于大多数的UI基特和Core
Animation方法)扶助二十三三十二线程绘制,-drawLayer:inContext:方法能够在三个线程中同时地并发调用,所以请翼翼小心地保管您在那么些情势中贯彻的绘图代码是线程安全的。

CAEAGLLayer

当iOS要拍卖高质量图形绘制,供给时正是OpenGL。应该说它应该是最终的拿手戏,至少对于非游戏的应用来讲是的。因为比较Core
Animation和UIkit框架,它不堪设想地复杂。

OpenGL提供了Core
Animation的根底,它是底层的C接口,直接和Motorola,GALAXY Tab的硬件通讯,极少地抽象出来的不二法门。OpenGL未有目的或然图层的后续概念。它只是轻巧地拍卖三角形。OpenGL中存有东西都是3D空间中有颜色和纹理的三角。用起来相当复杂和强劲,不过用OpenGL绘制iOS用户界面就供给过多居多的做事了。

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

因为OpenGL根本不会对您的始末张开假如,它能够绘制得格外快。利用OpenGL,你可以绘制任何你知道须求的集结音讯和形态逻辑的始末。所以重重游戏都爱好用OpenGL(这一个境况下,Core
Animation的限制就壹览无遗了:它优化过的始末类型并不一定能满意急需),可是如此借助,方便的中度抽象接口就没了。

在iOS
第55中学,苹果引进了三个新的框架叫做GLKit,它去掉了1部分安装OpenGL的复杂,提供了三个叫作CLKViewUIView4858.com ,的子类,帮您处理大多数的设置和制图工作。前提是各样三种的OpenGL绘图缓冲的尾巴部分可配置项依然要求您用CAEAGLLayer完成,它是CALayer的八个子类,用来显示任意的OpenGL图形。

大多状态下您都不需求手动设置CAEAGLLayer(要是用GLKView),过去的生活就不用再提了。越发的,我们将安装1个OpenGL
ES 2.0的上下文,它是今世的iOS设备的正经做法。

就算不要求GLKit也能够形成那整个,不过GLKit囊括了过多附加的做事,比如设置极端和壹些着色器,这几个都是类C语言叫做GLSL自包涵在先后中,同时在运营时载入到图片硬件中。编写GLSL代码和装置EAGLayer从不什么关系,所以大家将用GLKBaseEffect类将着色逻辑抽象出来。其余的作业,大家照旧会有过去的艺术。

在初始从前,你需求将GL基特和OpenGLES框架加入到你的门类中,然后就足以兑现清单6.第11四中学的代码,里面是设置3个GAEAGLLayer的至少工作,它选拔了OpenGL
ES 二.0 的绘图上下文,并渲染了1个死里逃生三角(见图陆.1伍).

清单6.14 用CAEAGLLayer绘图2个三角

#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

4858.com 26

图 6.15

图6.15 用OpenGL渲染的CAEAGLLayer图层

在叁个的确的OpenGL应用中,大家兴许会用NSTimerCADisplayLink周期性地每秒钟调用-drawRrame措施伍十五次,同时会将几何图形生成和制图分开以便不会每一回都重新生成三角形的终点(这样也能够让大家绘制别的的一对事物而不是3个三角形而已),可是上边这一个事例已经足足演示了绘图原则了。

Retina小图

你恐怕已经注意到了这个小图并不是以Retina的分辨率展现的。为了以显示器的原生疏辨率来渲染CATiledLayer
,大家需求设置图层的contentsScale来相称UIScreen的scale
属性:
tileLayer.contentsScale = [UIScreen mainScreen].scale;

风趣的是,tileSize是以像素为单位,而不是点,所以增大了contentsScale就机关有了暗许的小图尺寸(今后它是12八12八的点而不是25陆25六).所以,大家不要求手工业更新小图的尺寸恐怕在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设备上(总尺寸是拾2四1024,而不是2048204八)。那些普通都不会影响到用CATiledLayer正常呈现的图纸类型(比如照片和地图,他们在筹划上正是要支持加大减弱,能够在不一样的缩放条件下显得),可是也亟需在心底清楚。

  • #### CAEmitterLayer

在iOS
5中,苹果引进了三个新的CALayer子类叫做CAEmitterLayer。CAEmitterLayer是贰个高质量的粒子引擎,被用来创制实时例子动画如:平流雾,火,雨等等那一个意义。

CAEmitterLayer看上去像是多数CAEmitterCell的容器,那几个CAEmitierCell定义了一个例子效果。你将会为分化的例证效果定义3个或八个CAEmitterCell作为模版,同时CAEmitterLayer负责基于那么些模版实例化三个粒子流。三个CAEmitterCell类似于3个CALayer:它有贰个contents属性能够定义为二个CGImage,别的还有一部分可安装属性决定着表现和行事。我们不会对这个属性逐1开始展览详尽的描述,你们能够在CAEmitterCell类的头文件中找到。

小编们来举个例子。大家将采取在1圆中发出不相同速度和发光度的粒子创造三个火热炸的效益。清单陆.1三饱含了扭转爆炸的代码。图陆.1三是运作结果

AVPlayerLayer

最后二个图层类型是AVPlayerLayer。即便它不是Core
Animation框架的壹部分(AV前缀看上去像),AVPlayerLayer是有其他框架(AVFoundation)提供的,它和Core
Animation紧凑地整合在联合签字,提供了3个CALayer子类来显示自定义的内容类型。

AVPlayerLayer是用来在iOS上海人民广播电视台播摄像的。他是高端接口例如MPMoivePlayer的平底完成,提供了体现录像的底部调整。AVPlayerLayer的利用1二分轻松:你能够用+playerLayerWithPlayer:方式创设三个已经绑定了视频播放器的图层,或许你能够先创制贰个图层,然后用player天性绑定三个AVPlayer实例。

在大家开始此前,大家要求增添AVFoundation到大家的种类中。然后,清单6.15创办了3个简短的影视播放器,图6.16是代码运维结果。

清单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

4858.com 27

图 6.16

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

大家用代码创设了二个AVPlayerLayer,不过我们还是把它增添到了多个器皿视图中,而不是一向在controller中的主视图上足够。那样实在是为着能够行使机动布局限制使得图层在最中间;不然,一旦装备被旋转了大家就要手动重新放置地点,因为Core
Animation并不扶助活动大小和电动布局(见第3章『图层几何学』)。

当然,因为AVPlayerLayerCALayer的子类,它延续了父类的具有天性。我们并不会受限于要在贰个矩形中播放摄像;清单六.1陆示范了在3D,圆角,有色边框,蒙板,阴影等成效(见图6.一7).

清单陆.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];
}

4858.com 28

图六.一7 3D视角下的边框和圆角`AVPlayerLayer`

清单陆.一三 用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属性钦点了四个得以勾兑图片内容貌色的混合色。在演示中,我们将它设置为桔色。

  • 事例某一属性的变动范围。比如emissionRange属性的值是二π,那表示例子能够从360度Infiniti制地方反射出来。假若钦赐2个小片段的值,就足以创立出2个星型

  • 钦命值在时光线上的生成。比如,在示范中,大家将阿尔法Speed设置为-0.肆,就是说例子的折射率每过1秒就是减弱0.4,这样就有发出出去之后稳步时辰的效果。

CAEmitterLayer的习性它自身主宰着方方面面例子系统的任务和形态。1些性质比如birthRate,lifetime和celocity,那个属性在CAEmitterCell中也有。那些属性会以相乘的秘技效果在共同,那样你就可以用1个值来增长速度大概增添整个例子系统。其他值得一提到的性格有以下那些:

  • renderMode,调整着在视觉上粒子图片是哪些混合的。你也许已经注意到了示范中大家把它设置为kCAEmitterLayerAdditive,它完结了这么三个效果:合并例子重叠部分的亮度使得看上去越来越亮。借使大家把它设置为默许的kCAEmitterLayerUnordered,效果就没那么美观了(见图陆.14)

  • preservesDepth,是或不是将3D例子系统平面化到3个图层(暗中认可值)或然能够在3D空间中混合其余的图层

4858.com 29

6.14.png

  • #### CAEAGLLayer

当iOS要拍卖高品质图形绘制,须求时就是OpenGL。应该说它应有是终极的绝艺,至少对于非游戏的施用来讲是的。因为比较Core
Animation和UIkit框架,它不可名状地复杂。

OpenGL提供了Core
Animation的基础,它是底层的C接口,直接和红米,华为平板的硬件通讯,极少地抽象出来的艺术。OpenGL未有目的可能图层的接轨概念。它只是简单地处理三角形。OpenGL中颇具东西都以3D空间中有颜色和纹理的三角形。用起来分外复杂和强有力,不过用OpenGL绘制iOS用户分界面就供给多多广大的行事了。

为了可以以高品质使用Core
Animation,你须要剖断你须要绘制哪一种内容(矢量图形,例子,文本,等等),但后选拔适宜的图层去显示那几个剧情,Core
Animation中唯有局地项指标剧情是被中度优化的;所以一旦你想绘制的东西并不能够找到专业的图层类,想要得到高质量就比较费事情了。

因为OpenGL根本不会对您的始末展开假如,它能够绘制得至相当慢。利用OpenGL,你能够绘制任何你精晓须要的集聚新闻和形态逻辑的始末。所以广大游戏都爱好用OpenGL(那一个意况下,Core
Animation的限量就明摆着了:它优化过的始末类型并不一定能满意急需),可是那样借助,方便的中度抽象接口就没了。

在iOS
第55中学,苹果引进了贰个新的框架叫做GLKit,它去掉了有些安装OpenGL的错综复杂,提供了一个称作CLKView的UIView的子类,帮你处理大多数的装置和制图工作。前提是应有尽有的OpenGL绘图缓冲的最底层可配备项照旧需求你用CAEAGLLayer完毕,它是CALayer的八个子类,用来展示任意的OpenGL图形。

大繁多情景下您都不需求手动设置CAEAGLLayer(假若用GLKView),过去的光景就不要再提了。尤其的,大家将安装贰个OpenGL
ES 二.0的上下文,它是当代的iOS设备的正规化做法。

固然不须求GLKit也能够实现那1体,不过GL基特囊括了众多外加的做事,比如设置终点和部分着色器,这么些都是类C语言叫做GLSL自包蕴在先后中,同时在运维时载入到图片硬件中。编写GLSL代码和设置EAGLayer没有啥关联,所以大家将用GLKBaseEffect类将着色逻辑抽象出来。其余的作业,大家照旧会有过去的法子。

在起来以前,你必要将GLKit和OpenGLES框架加入到您的类型中,然后就足以达成清单陆.第11④中学的代码,里面是安装三个GAEAGLLayer的足足职业,它使用了OpenGL
ES 二.0 的绘图上下文,并渲染了三个化险为夷三角(见图陆.15).

总结

那一章大家简要概述了有的专用图层以及用他们落到实处的壹部分效应,大家只是驾驭到这么些图层的肤浅,像CATiledLayerCAEMitterLayer那一个类能够独立写壹章的。不过,重点是记住CALayer是用处十分的大的,而且它并不曾为有着只怕的光景进行优化。为了赢得Core
Animation最佳的品质,你必要为您的做事选对正确的工具,希望您能够挖掘那么些区别的CALayer子类的意义。
那壹章大家因而CAEmitterLayerAVPlayerLayer类轻便地接触到了一些卡通,在第3章,大家将继续深入商讨动画,就从隐式动画开端。

清单6.1四 用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

4858.com 30

6.15.png

在一个着实的OpenGL应用中,我们兴许会用NSTimer或CADisplayLink周期性地每分钟调用-draw中华Vrame方法伍15遍,同时会将几何图形生成和制图分开以便不会每便都再一次生成三角形的巅峰(这样也足以让我们绘制别的的部分事物而不是3个三角形形而已),然而下面那么些例子已经够用演示了绘图原则了。

  • #### AVPlayerLayer

末段二个图层类型是AVPlayerLayer。就算它不是Core
Animation框架的一某个(AV前缀看上去像),AVPlayerLayer是有其余框架(AVFoundation)提供的,它和Core
Animation紧凑地组成在一块,提供了1个CALayer子类来体现自定义的始末类型。

AVPlayerLayer是用来在iOS上播报录制的。他是高等接口例如MP莫伊vePlayer的最底层完毕,提供了展示摄像的平底调控。AVPlayerLayer的施用格外简单:你能够用+playerLayerWithPlayer:方法创制三个早已绑定了摄像播放器的图层,大概你可以先创建二个图层,然后用player属性绑定三个AVPlayer实例。

在我们开首从前,我们供给增多AVFoundation到大家的门类中。然后,清单陆.15创办了三个大致的影片播放器,图陆.16是代码运维结果。

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

清单陆.一五 用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

4858.com 31

6.16.png

咱俩用代码创造了一个AVPlayerLayer,可是大家照例把它增加到了1个容器视图中,而不是平素在controller中的主视图上加上。那样其实是为了能够选拔机关布局限制使得图层在最中间;不然,1旦装备被旋转了大家就要手动重新放置位置,因为Core
Animation并不协助电动大小和活动布局(见第贰章『图层几何学』)。

自然,因为AVPlayerLayer是CALayer的子类,它继续了父类的具有本性。大家并不会受限于要在四个矩形中播放摄像;清单6.16示范了在3D,圆角,有色边框,蒙板,阴影等职能(见图6.一七).

清单陆.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];
    }
总结

这1章大家大约概述了有的专用图层以及用他们落实的1对作用,我们只是精晓到那一个图层的皮毛,像CATiledLayer和CAEMitterLayer这么些类能够独自写一章的。可是,重点是念念不忘CALayer是用处十分的大的,而且它并不曾为全数希望的光景举行优化。为了获取Core
Animation最棒的属性,你需求为你的劳作选对正确的工具,希望您可见挖掘那么些差异的CALayer子类的法力。
那一章我们透过CAEmitterLayer和AVPlayerLayer类轻松地接触到了有的卡通,在其次章,我们将承袭深刻钻研动画,就从隐式动画起始。

发表评论

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

网站地图xml地图
Copyright @ 2010-2019 美高梅手机版4858 版权所有