专用图层

CAScrollLayer

对此一个未改换的图层,它的bounds和它的frame是相通的,frame个性是由bounds属性自动测算而出的,所以修正任性叁个值都会更新任何值。

而是假诺您只想显示三个大图层里面的一小部分吧。譬如说,你或许有三个不小的图形,你希望客户能够随便滑动,可能是叁个多少或文本的长列表。在一个规范的iOS应用中,你可能会用到UITableView或是UIScrollView,不过对于单身的图层来讲,什么会等价于刚(Yu-Gang卡塔尔(قطر‎刚提到的UITableViewUIScrollView呢?

在第二章中,大家斟酌了图层的contentsRect特性的用法,它确实是能够解决在图层中型小型地点显得大图片的缓慢解决方法。然则只要你的图层富含子图层那它就不是一个那些好的缓慢解决方案,因为,那样做的话每一趟你想『滑动』可视区域的时候,你就要求手工业重新计算并更新具备的子图层地方。

当时就要求CAScrollLayer了。CAScrollLayer有一个-scrollToPoint:艺术,它自动适应bounds的原点以便图层内容现身在滑行的地点。注意,那正是它做的具备职业。前面提到过,Core Animation并不管理客商输入,所以CAScrollLayer并不承当将触摸事件调换为滑行事件,既不渲染滚动条,也不贯彻任何iOS钦赐行为比方滑动反弹(当视图滑动非常多了它的边界的将会反弹回正确的地点)。

让大家来用CAScrollLayer来普及一个着力的UIScrollView替代品。大家将会用CAScrollLayer用作视图的宿主图层,并创办五个自定义的UIView,然后用UIPanGestureRecognizer贯彻触摸事件响应。这段代码见清单6.10. 图6.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)。图层内容极有非常的大可能率滑出视图的边际并不过滑下去。CAScrollLayer并不曾相似UIScrollViewcontentSize的属性,所以当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:办法从图层树中搜索并找到第2个可用的CAScrollLayer,然后滑动它使得钦点点改为可视的。scrollRectToVisible:方法完毕了同生机勃勃的事情只可是是效果在一个矩形上的。visibleRect性情决定图层(假诺存在的话)的哪部分是如今的可视区域。假让你和煦完毕那一个点子就能相对轻便通晓有些,可是CAScrollLayer帮您省了这些劳顿,所以当提到到贯彻图层滑动的时候就足以用上了。

 

专项使用图层

复杂的团队都以特地化的

Catharine R. Stimpson

到最近结束,大家已经探求过CALayer类了,同时我们也领悟到了有的不胜管用的绘图和卡通效果。可是Core Animation图层不独有能成效于图片和颜料而已。本章就能够学习别的的片段图层类,进一层强盛使用Core Animation绘图的力量。

CAShapeLayer

在第四章『视觉效果』我们学习到了不行使图片的意况下用CGPath去组织大肆形状的阴影。假若大家能用相通的法子开创相似形状的图层就好了。

CAShapeLayer是叁个透过矢量图形实际不是bitmap来绘制的图层子类。你内定诸如颜色和线宽等品质,用CGPath来定义想要绘制的图样,最终CAShapeLayer就机关渲染出来了。当然,你也能够用Core Graphics直接向原始的CALyer的源委中绘制叁个门路,相比直下,使用CAShapeLayer有以下部分独特之处:

  • 渲染快速。CAShapeLayer动用了硬件加快,绘制同豆蔻梢头图形会比用Core Graphics快超级多。
  • 高速利用内部存款和储蓄器。几个CAShapeLayer无需像通常CALayer意气风发致成立一个留宿图形,所以不管有多大,都不会占用太多的内部存储器。
  • 不会被图层边界剪开除。一个CAShapeLayer能够在分界之外绘制。你的图层路径不会像在应用Core Graphics的平日CALayer同风流倜傥被剪开除(如大家在其次章所见)。
  • 不会现出像素化。当您给CAShapeLayer做3D改造时,它不像三个有寄宿图的日常图层同样变得像素化。

创建一个CGPath

CAShapeLayer能够用来绘制全数能够透过CGPath来代表的形状。那一个形象不分明要关闭,图层路线也不必然要不可破,事实上你能够在一个图层上制图大多少个例外的形制。你能够决定一些性质比方lineWith(线宽,用点表示单位),lineCap(线条结尾的标准),和lineJoin(线条之间的结合点的标准);不过在图层层面你独有二回机遇设置这么些属性。假诺您想用不一样颜色或风格来绘制八个造型,就只能为每个造型打算三个图层了。

清单6.1 的代码用叁个CAShapeLayer渲染八个精短的火柴人。CAShapeLayer属性是CGPathRef项目,不过大家用UIBezierPath扶持类创设了图层路线,那样大家就毫无考虑人工释放CGPath了。图6.1是代码运营的结果。纵然还不是很康健,不过到底知道了概略对啊!

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

图片 3

图6.1

图6.1 用CAShapeLayer绘制一个简短的火柴人

圆角

其次章里面涉及了CAShapeLayer为成立圆角视图提供了二个主意,正是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作为视图的宿主图层,实际不是丰盛四个子视图(图层蒙板的详实表明见第四章『视觉效果』)。

CATextLayer

顾客分界面是力所不比从二个单身的图纸里面塑造的。三个企划非凡的Logo可以很好地球表面现二个按键或控件的来意,可是你迟早都要索要叁个精确的过时风格的文书标签。

借使您想在贰个图层里面展现文字,完全能够依赖图层代理直接将字符串使用Core Graphics写入图层的从头到尾的经过(这正是UILabel的精粹)。假若通过寄宿于图层的视图,直接在图层上操作,那其实一定繁杂。你要为每贰个突显文字的图层创建四个能像图层代理相通干活的类,还要逻辑上判定哪些图层必要出示哪个字符串,更别提还要记下差别的字体,颜色等风流倜傥多级语无伦次的事物。

刚巧的是这个都以不必要的,Core Animation提供了三个CALayer的子类CATextLayer,它以图层的情势满含了UILabel差十分的少全部的绘图性情,况且额外提供了某些新的性状。

同样,CATextLayer也要比UILabel渲染得快得多。很稀少人明白在iOS 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

图片 4

图6.2

图6.2 用CATextLayer来展示五个纯文本标签

即使你用心看这一个文件,你会意识一个想不到的地点:这一个文件有部分像素化了。那是因为并从未以Retina的艺术渲染,第二章提到了这么些contentScale品质,用来调节图层内容应当以怎么着的分辨率来渲染。contentsScale并不关注显示屏的拉伸因素而三回九转默感到1.0。假设大家想以Retina的品质来显示文字,我们就得手动地安装CATextLayercontentsScale属性,如下:

textLayer.contentsScale = [UIScreen mainScreen].scale;

那般就缓慢解决了那几个主题材料(如图6.3)

图片 5

图 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用来渲染字体风格的机制,它以一定的不二法门来调整钦定范围内的字符串的原本消息,举例字体,颜色,字重,斜体等。

富文本

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完毕三个富文本标签。

#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

图片 6

图 6.4

图6.4 用CATextLayer完成三个富文本标签。

行距和字距

有重中之重提一下的是,由于绘制的达成机制分化(Core Text和WebKit),用CATextLayer渲染和用UILabel渲染出的文件行距和字距亦非不尽相仿的。

两岸的歧异程度(由运用的字体和字符决定)不问可以知道挺小,不过假如您想正确的来得普通便签和CATextLayer就确定要牢牢记住那或多或少。

UILabel的代替品

我们曾经证实了CATextLayerUILabel具有越来越好的性质表现,同不经常候还只怕有特其余布局选项何况在iOS 5上支撑富文本。但是与日常的标签相比较来讲会愈加繁缛一些。假诺大家实在在需要三个UILabel的可用代替品,最棒是可以在Interface Builder上开创我们的价签,并且尽量地像雷同的视图同样健康工作。

咱俩应当继续UILabel,然后增加二个子图层CATextLayer相提并论写展现文本的法门。不过仍旧会有由UILabel-drawRect:办法创制的空寄宿图。並且由于CALayer不扶助活动缩放和电动布局,子视图实际不是积极追踪视图边界的高低,所以每便视图大小被转移,大家只可以手动更新子图层的界线。

咱俩真正想要的是三个用CATextLayer用作宿主图层的UILabel子类,那样就足以趁机视图自动调度大小何况也未有冗余的寄宿图啦。

就疑似大家在首先章『图层树』研究的意气风发律,每二个UIView都以过夜在一个CALayer的亲自去做上。这些图层是由视图自动创设和治本的,这大家得以用别的图层类型取而代之么?生龙活虎旦被成立,大家就不只怕代替这一个图层了。可是假如我们后续了UIView,那大家就足以重写 layerClass艺术使得在创设的时候能回来多少个例外的图层子类。UIView会在起首化的时候调用 layerClass办法,然后用它的回到类型来创建宿主图层。

项目清单6.4 演示了七个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作为宿主图层的另风华正茂功利就是视图自动安装了contentsScale属性。

在此个轻松的事例中,咱们只是达成了UILabel的一片段风格和结构属性,可是有个别再修改一下大家就能够创设一个支撑UILabel享有机能照旧更加多职能的LayerLabel类(你能够在部分线上的开源项目中找到)。

假如您绸缪帮助iOS 6及以上,基于CATextLayer的竹签或许就有稍许局限性。但是总得来讲,假诺想在app里面丰裕利用CALayer子类,用 layerClass来创设基于分裂图层的视图是四个简约可复用的方式。

CATransformLayer

当我们在社团复杂的3D事物的时候,要是能够组织独立成分就太方便了。比方说,你想创建一个孩子的胳膊:你就须要鲜明哪部分是亲骨血的手法,哪蓬蓬勃勃部分是子女的膀子,哪部分是儿女的肘,哪朝气蓬勃部分是孩子的膀子,哪部分是男女的肩头等等。

道理当然是那样的是允许单独地移动每个地区的啦。以肘为指点会移动前臂和手,并非肩部。Core Animation图层十分轻易就可以令你在2D条件下做出如此的层级系列下的调换,然则3D境况下就不太或许,因为全数的图层都把她的子女都平面化到叁个风貌中(第五章『转变』有提到)。

CATransformLayer削株掘根了那些难点,CATransformLayer分裂于普通的CALayer,因为它不可能显得它自身的故事情节。独有当存在了二个能功用于子图层的改动它才真正存在。CATransformLayer并不平面化它的子图层,所以它能够用于组织二个层级的3D结构,举个例子笔者的膀子示例。

用代码创造一个手臂需求超多的代码,所以笔者就演示得更简约一些啊:在第五章的立方体示例,我们将通过旋转camara来缓慢解决图层平面化难题并非像立方体示例代码中用的sublayerTransform。那是三个老大正确的技巧,然则只好作用域单个对象上,假诺您的情况富含五个立方,那大家就不可能用这些技巧单独旋转他们了。

那么,就让我们来试意气风发试CATransformLayer啊,第1个难题就来了:在第五章,大家是用多少个视图来布局了我们的立方体,实际不是独立的图层。大家无法在不打乱已某些视图档次的前提下在三个自个儿不是有寄宿图的图层中放置多少个寄宿图图层。大家得以成立二个新的UIView子类寄宿在CATransformLayer(用 layerClass艺术)之上。不过,为了简化案例,大家只是重建了八个独自的图层,实际不是运用视图。那表示大家不能像第五章相仿在立方体表面呈现开关和标签,可是大家今后也用不到这几个天性。

项目清单6.5正是代码。我们以大家在第五章使用过的同样基本逻辑放置立方体。可是并不像早先这样直接将立方面增加到容器视图的宿主图层,大家将她们放置到叁个CATransformLayer中开创多个独立的立方体对象,然后将多少个如此的立方体放进容器中。大家随意地给立方面染色以将她们分别开来,那样就不用靠标签可能光亮来分别他们。图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

图片 7

图 6.5

图6.5 同一视角下的俩不风流洒脱转换的立方体

CAGradientLayer

CAGradientLayer是用来生成二种或更加多颜色平滑渐变的。用Core Graphics复制三个CAGradientLayer并将内容绘制到一个枯燥没有味道图层的寄宿图也有希望的,不过CAGradientLayer的着实好处在于绘制使用了硬件加快。

幼功渐变

我们将从一个简练的红变蓝的对角线渐变早先(见清单6.6).那几个渐变色彩放在三个数组中,并赋给colors品质。这几个数组成员接纳CGColorRef类型的值(并不是从NSObject派生而来),所以大家要用通过bridge转变以作保编写翻译日常。

CAGradientLayer也有startPointendPoint本性,他们说了算了渐变的方向。那多个参数是以单位坐标系进行的概念,所以左上角坐标是{0, 0},右下角坐标是{1, 1}。代码运营结果如图6.6

项目清单6.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

图片 8

图 6.6

图6.6 用CAGradientLayer落到实处简单的三种颜色的对角线渐变

多种渐变

若果你愿意,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);
}

图片 9

图 6.7

图6.7 用locations布局偏移至左上角的三色渐变

CAReplicatorLayer

CAReplicatorLayer的目标是为了赶快转换多数近似的图层。它会绘制四个或八个图层的子图层,并在每一种复制体上行使不一样的调换。看上去演示能够更为解释那个,大家来写个例证吗。

再一次图层(Repeating Layers)

项目清单6.8中,大家在显示屏的中游创设了三个小日光黄方块图层,然后用CAReplicatorLayer更动十一个图层组成三个圆形。instanceCount天性钦定了图层供给重新多少次。instanceTransform点名了二个CATransform3D3D转换(这种地方下,下大器晚成图层的位移和旋转将会移动到圆圈的下贰个点)。

转变是逐级加多的,每种实例都是争执于前黄金时代实例构造。那正是为何那么些复制体最后不汇合世在同意地方上,图6.8是代码运行结果。

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

图片 10

图 6.8

图6.8 用CAReplicatorLayer创办大器晚成圈图层

用心到当图层在再度的时候,他们的颜色也在转换:那是用instanceBlueOffsetinstanceGreenOffset质量完成的。通过稳步回降卡其灰和蛋黄通道,大家日益将图层颜色转变来了丙辰革命。那几个复制效果看起来很帅,不过CAReplicatorLayer确实使用到骨子里程序上的气象比方:叁个娱乐中程导弹弹的轨道云,可能粒子爆炸(尽管iOS 5已经引进了CAEmitterLayer,它更合乎成立任性的粒子效果)。除了这些之外,还会有一个其进行使是:反射。

反射

使用CAReplicatorLayer并利用一个负比例调换于四个复制图层,你就足以创建钦点视图(或任何视图档案的次序)内容的镜像图片,那样就创办了一个实时的『反射』效果。让大家来品尝完结那一个创新意识:内定三个后续于UIViewReflectionView,它会自动发出内容的反光功效。完成这几个效能的代码超级粗略(见清单6.9),实际上用ReflectionView落实那么些效应会更轻巧,大家只要求把ReflectionView的实例放置于Interface Builder(见图6.9),它就能够实时生成子视图的反射,而不供给其他代码(见图6.10).

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

图片 11

图 6.9

图6.9 在Interface Builder中使用ReflectionView

图片 12

图 6.10

图6.10 ReflectionView机关实时产生反射作用。

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

CAScrollLayer

对此一个未改造的图层,它的bounds和它的frame是千篇大器晚成律的,frame品质是由bounds质量自动总结而出的,所以改进放肆三个值都会更新任何值。

但是倘诺你只想体现二个大图层里面包车型客车一小部分呢。举个例子说,你恐怕有八个十分大的图片,你愿意客户可以轻松滑动,恐怕是叁个数量或文本的长列表。在七个优良的iOS应用中,你可能会用到UITableView或是UIScrollView,不过对于单身的图层来讲,什么会等价于刚先生刚提到的UITableViewUIScrollView呢?

在其次章中,我们商量了图层的contentsRect本性的用法,它真的是能力所能达到搞定在图层中型Mini地点显得大图片的解决办法。然则倘使您的图层富含子图层那它就不是四个特出好的建设方案,因为,这样做的话每趟你想『滑动』可视区域的时候,你就供给手工业重新总计并立异具有的子图层地点。

其临时候就须求CAScrollLayer了。CAScrollLayer有一个-scrollToPoint:措施,它自动适应bounds的原点以便图层内容出未来滑行的地点。注意,那正是它做的有所专门的学业。前面提到过,Core Animation并不管理客商输入,所以CAScrollLayer并不担当将触摸事件转变为滑行事件,既不渲染滚动条,也不完结任何iOS钦赐行为比如滑动反弹(当视图滑动相当多了它的边界的将会反弹回准确之处)。

让大家来用CAScrollLayer来普及一个主导的UIScrollView代替品。大家将会用CAScrollLayer作为视图的宿主图层,并创立一个自定义的UIView,然后用UIPanGestureRecognizer贯彻触摸事件响应。这段代码见清单6.10. 图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

图片 13

图 6.11

图6.11 用UIScrollView创立多少个凑合的滑行视图

不同于UIScrollView,大家定制的滑行视图类并从未落到实处任何款式的疆界检查(bounds checking)。图层内容极有相当的大概率滑出视图的境界并Infiniti滑下去。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帮你省了那个劳动,所以当提到到完毕图层滑动的时候就能够用上了。

CATiledLayer

稍许时候你恐怕必要绘制一个超级大的图片,不足为道的例证就是二个高像素的肖像照旧是地球表面包车型客车详细地图。iOS应用流畅运维在内部存款和储蓄器受限的设备上,所以读取整个图片到内部存款和储蓄器中是不明智的。载入大图恐怕会一定地慢,这么些对您看上去比较方便的做法(在主线程调用UIImage-imageNamed:办法依旧-imageWithContentsOfFile:措施)将会窒碍你的客商分界面,起码会引起动画卡顿现象。

能高效绘制在iOS上的图纸也许有三个大大小小节制。全部展现在显示屏上的图形最后都会被转变为OpenGL纹理,同一时间OpenGL有多少个最大的纹路尺寸(经常是2048*2048,或4096*4096,这一个决议于设备型号)。若是您想在单个纹理中呈现三个比那大的图,尽管图片已经存在于内存中了,你依然会遇上非常大的属性难点,因为Core Animation强制用CPU管理图片实际不是越来越快的GPU(见第12章『速度的曲调』,和第13章『高效绘图』,它特别详实地演说了软件绘图和硬件绘制)。

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

小片裁剪

以此示例中,大家将会从八个2048*2048分辨率的雪人图片入手。为了可以从CATiledLayer中受益,我们必要把这些图形裁切成很多小部分的图纸。你能够由此代码来成功这事情,可是假如你在运维时读入整个图片并裁切,那CATiledLayer那个具备的性质优点就损失殆尽了。理想状态下来讲,最棒能够每一种步骤来落到实处。

项目清单6.11 演示了一个简单的Mac OS命令路程序,它用CATiledLayer将二个图形裁剪成小图并积攒到分化的文本中。

项目清单6.11 裁剪图片成小图的终点程序

#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: @"_i_i.jpg", x, y];
            [data writeToFile:path atomically:NO];
            }
        }
    }
    return 0;
}

本条顺序将2048*2048分辨率的雪人图案裁剪成了陆十二个不等的256*256的小图。(256*256是CATiledLayer的默许小图大小,默许大小可以透过tileSize天性改善)。程序选拔二个图片路线作为命令行的率先个参数。大家得以在编写翻译的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:

以此顺序一定底蕴,不过能够随便地扩充扶持额外的参数比方小图大小,恐怕导出格式等等。运行结果是陆拾二个新图的行列,如下边命名:

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

既是大家有了裁切后的小图,我们将在让iOS程序用到他俩。CATiledLayer很好地和UIScrollView合併在联合。除了安装图层和滑动视图边界以适配整个图片大小,大家的确要做的便是贯彻-drawLayer:inContext:主意,当须要载入新的小图时,CATiledLayer就能调用到那几个主意。

清单6.12示范了代码。图6.12是代码运转结果。

项目清单6.12 三个轻易易行的轮转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_i_i", x, y];
    NSString *imagePath = [[NSBundle mainBundle] pathForResource:imageName ofType:@"jpg"];
    UIImage *tileImage = [UIImage imageWithContentsOfFile:imagePath];

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

图片 14

图 6.12

图6.12 用UIScrollView滚动CATiledLayer

当您滑动这一个图形,你会意识当CATiledLayer载入小图的时候,他们会淡入到界面中。那是CATiledLayer的暗中认可行为。(你大概早已在iOS 6以前的苹果地图程序中见过这些功能)你能够用fadeDuration属性改换淡入时间长度或直接禁止使用掉。CATiledLayer(分化于大部分的UIKit和Core Animation方法)帮助八线程绘制,-drawLayer:inContext:措施能够在八个线程中而且地并发调用,所以请从长远的角度考虑地保险您在这里个法子中落到实处的绘图代码是线程安全的。

Retina小图

您恐怕已经注意到了那么些小图并非以Retina的分辨率展现的。为了以显示器的原生疏辨率来渲染CATiledLayer,我们供给安装图层的contentsScale来匹配UIScreenscale属性:

tileLayer.contentsScale = [UIScreen mainScreen].scale;

有意思的是,tileSize是以像素为单位,实际不是点,所以增大了contentsScale就机关有了默许的小图尺寸(以后它是128*128的点实际不是256*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设备上(总尺寸是1024*1024,而不是2048*2048)。那么些普通都不会耳熟能详到用CATiledLayer例行彰显的图纸类型(比方照片和地图,他们在规划上就是要协助加大减少,能够在差别的缩放条件下显得),不过也须要在心中亮堂。

CAEmitterLayer

在iOS 5中,苹果引入了二个新的CALayer子类叫做CAEmitterLayerCAEmitterLayer是一个高质量的粒子引擎,被用来创立实时例子动漫如:冰雾,火,雨等等这么些功用。

CAEmitterLayer看起来疑似超多CAEmitterCell的容器,这些CAEmitierCell概念了一个例子效果。你将会为区别的例证效果定义多个或多少个CAEmitterCell用作模版,同时CAEmitterLayer担负依据那些模版实例化叁个粒子流。一个CAEmitterCell恍如于一个CALayer:它有二个contents天性能够定义为三个CGImage,其它还应该有生龙活虎部分可安装属性决定着表现和作为。大家不会对这几个属性逐生机勃勃举办详细的呈报,你们能够在CAEmitterCell类的头文件中找到。

我们来举例。我们将运用介意气风发圆中发出区别速度和反射率的粒子创立多少个销路好炸的效果与利益。项目清单6.13包罗了转移爆炸的代码。图6.13是运作结果

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

图片 15

图 6.13

图6.13 火焰爆炸效果

CAEMitterCell的特性基本上可以分成三种:

  • 这种粒子的某意气风发性质的开始值。比方,color特性钦点了四个方可勾兑图片内容貌色的混合色。在示范中,咱们将它设置为桔色。
  • 粒子某豆蔻梢头性质的更改范围。举例emissionRange品质的值是2π,这代表粒子可以从360度自由地点反射出来。要是钦赐三个小片段的值,就能够创制出三个圆柱形。
  • 钦定值在时刻线上的改造。比如,在示范中,大家将alphaSpeed设置为-0.4,便是说粒子的反射率每过风流倜傥秒正是减少0.4,那样就有发出出去之后慢慢消失的功用。

CAEmitterLayer的属性它自身调控着漫天粒子系统的职位和形制。一些性质例如birthRatelifetimecelocity,那么些属性在CAEmitterCell中也许有。那些属性会以相乘的方法效果在合作,那样您就足以用二个值来加速大概扩大整个粒子系统。别的值得说起的属性有以下这一个:

  • preservesDepth,是或不是将3D粒子系统平面化到贰个图层(暗中同意值)恐怕能够在3D空间中混合其余的图层。
  • renderMode,调整着在视觉上粒子图片是怎么着混合的。你恐怕早已注意到了示范中大家把它设置为kCAEmitterLayerAdditive,它完成了那样多个效果:归总粒子重叠部分的亮度使得看上去更加亮。若是大家把它设置为暗中同意的kCAEmitterLayerUnordered,效果就没那么狼狈了(见图6.14)。

图片 16

图 6.14

图6.14 幸免混色之后的火苗粒子

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 5中,苹果引进了贰个新的框架叫做GL基特,它去掉了部分设置OpenGL的复杂,提供了贰个称作CLKViewUIView的子类,帮你管理超越二分之后生可畏的装置和制图职业。前提是美妙绝伦的OpenGL绘图缓冲的最底层可配备项依旧须求你用CAEAGLLayer完成,它是CALayer的一个子类,用来突显跋扈的OpenGL图形。

绝大多数动静下你都不要求手动设置CAEAGLLayer(借使用GLKView),过去的光景就绝不再提了。特别的,我们将安装三个OpenGL ES 2.0的上下文,它是今世的iOS设备的标准做法。

就算无需GLKit也得以成功那全数,但是GLKit囊括了比比较多额外的行事,举例设置极端微风流浪漫部分着色器,那一个都是类C语言叫做GLSL自包括在程序中,相同的时间在运营时载入到图片硬件中。编写GLSL代码和设置EAGLayer尚无怎么关系,所以大家将用GLKBaseEffect类将着色逻辑抽象出来。别的的专门的职业,我们依旧会有过去的办法。

在最早在此之前,你须要将GLKit和OpenGLES框架参加到你的体系中,然后就足以兑现清单6.14中的代码,里面是安装一个GAEAGLLayer的起码工作,它接受了OpenGL ES 2.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

图片 17

图 6.15

图6.15 用OpenGL渲染的CAEAGLLayer图层

在几个的确的OpenGL应用中,我们恐怕会用NSTimerCADisplayLink周期性地每分钟调用-drawRrame办法五拾陆回,同有时候会将几何图形生成和制图分开以便不会每便都再也生成三角形的尖峰(这样也得以让我们绘制别的的部分事物并非叁个三角形而已),但是上面这些事例已经丰硕演示了绘图原则了。

AVPlayerLayer

提及底叁个图层类型是AVPlayerLayer。固然它不是Core Animation框架的一片段(AV前缀看上去像),AVPlayerLayer是有别的框架(AVFoundation)提供的,它和Core Animation紧凑地组成在一起,提供了三个CALayer子类来显示自定义的内容类型。

AVPlayerLayer是用来在iOS上广播录像的。他是高档接口比如MPMoivePlayer的平底达成,提供了显示录像的平底调控。AVPlayerLayer的应用非常轻松:你能够用 playerLayerWithPlayer:措施创造几个早就绑定了摄像播放器的图层,或然你能够先成立二个图层,然后用player属性绑定三个AVPlayer实例。

在大家起先在此之前,大家须求增多AVFoundation到大家的品种中。然后,清单6.15创设了三个轻松易行的录制播放器,图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

图片 18

图 6.16

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

我们用代码创制了贰个AVPlayerLayer,不过大家照旧把它增多到了叁个器皿视图中,并非一向在controller中的主视图上助长。那样实乃为着能够动用电动构造约束使得图层在最中间;不然,生龙活虎旦装备被旋转了大家将要手动重新放置地点,因为Core Animation并不援救电动大小和机动布局(见第三章『图层几何学』)。

当然,因为AVPlayerLayerCALayer的子类,它继续了父类的有着本性。大家并不会受限于要在三个矩形中播放录像;清单6.16示范了在3D,圆角,有色边框,蒙板,阴影等成效(见图6.17).

清单6.16 给录制增添转换,边框和圆角

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

图片 19

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

总结

那风度翩翩章大家大约概述了一部分专项使用图层以致用他们完成的后生可畏对效应,大家只是理解到那些图层的肤浅,像CATiledLayerCAEMitterLayer这一个类能够独自写风华正茂章的。然则,注重是难忘CALayer是用场非常的大的,而且它并未为保有希望的情景举办优化。为了博取Core Animation最棒的习性,你供给为您的干活选对精确的工具,希望你能够发掘这个分裂的CALayer子类的功效。
那大器晚成章大家透过CAEmitterLayerAVPlayerLayer类轻便地接触到了有个别动画片,在第二章,我们将一连深刻钻研动漫,就从隐式动漫开首。

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

本文由星彩网app下载发布于计算机编程,转载请注明出处:专用图层

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。