编程

当前位置:永利皇宫463登录 > 编程 > 一定图层,图层几何

一定图层,图层几何

来源:http://www.makebuLuo.com 作者:永利皇宫463登录 时间:2019-09-12 14:29

在你最初使用变形前您应当牢记那点,因为当一个图层旋转或缩放,它的frame反馈被变形图层在父图层中所占用的矩形区域在总轴的照射,那意味着frame的宽和高不再相配bounds

我们得以手动使用独立的直线和弧线来创立圆角矩形路径,但UIBezierPath实在有一对机关创造圆角矩形的方便人民群众方法。接下来的代码片断发生八个三圆角向来角的不二秘诀:

大家在那章的CAEmitterLayerAVPlayerLayer中接触到了某个卡通。在第二部分中,大家从隐式动画最早深刻摸底动画属性。

图片 1图3.8 古铜黑视图在新民主主义革命视图视图等级次序之下

在第4章“视觉特效”中你学会了哪些用CGPath来平素创立四个阴影形状,实际不是应用图像。如若您能够用平等的措施开创图层形状将是极好的。

表6.16 给视频增加变形、边框和圆角override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 判断横屏 let screenSize = UIScreen.mainScreen().applicationFrame.size if (screenSize.width > screenSize.height) { // 获得视频URL let URL = NSBundle.mainBundle().URLForResource("Ship", withExtension: "mp4") // 创建播放器及其图层 let player = AVPlayer let playerLayer = AVPlayerLayer(player: player) // 设置播放器图层帧并加到视图中 playerLayer.frame = self.containerView.bounds self.containerView.layer.addSublayer(playerLayer) // 变形图层 var transform = CATransform3DIdentity transform.m34 = -1.0 / 500.0 transform = CATransform3DRotate(transform, CGFloat, 1, 1, 0) playerLayer.transform = transform // 添加圆角和边框 playerLayer.masksToBounds = true playerLayer.cornerRadius = 20.0 playerLayer.borderColor = UIColor.redColor().CGColor playerLayer.borderWidth = 5.0 // 播放视频 player.play() }}

静心并从未用来depth属性用来填补bounds的宽和高,图层本质上是平面物体。你可以把它们想你成是单独的二维的硬纸壳但足以用胶水粘成人中学空的折纸似的三个维度结构。

图片 2图6.8 用CAReplicatorLayer创设的图层环

这一章提供了成都百货上千一定图层类型的总览,以及利用它们达到的成效。大家差不四只讲授了基础知识;有个别类如CATiledLayerCAEmitterLayer的学问能够作为独立的章节来讲。不过,关键要铭记在心的是CALayer是万金油,并不曾为持有绘制景况开展优化。为了获得Core Animation的特等质量,你需求活动选拔最契合的工具,希望您早已碰到激励来浓厚摸底差别的CALayer子类及它们的技巧。

在第4章“视觉特效”中大家将执教一些'Core Animation'的图层表现个性。

故而让大家品尝用CATransformLayer来替代。第贰个要缓和的难题是我们在第5章用的视图并非独立的图层的创设构方体的。大家无法把一个视图后的图层放在另八个不是本人视图的图层里而不弄乱视图档案的次序。我们得以创设UIVIew的一个遵照CATransformLayer的子类(使用+layerClass措施),但为了在我们的例子中简化一切,让大家再次用独立的图层来创制立方体。那代表大家无法像第5章同样在其上海展览中心示按键和标签,但大家前几天并无需那样做。

当然,因为AVPlayerLayerCALayer的二个子类,它继续了它有着的表征。大家不自然要在贰个简短的矩形中播放录像;通过6.16中的额外轮代理公司码,大家得以3D旋转录制并加多圆角、彩色边框、遮罩、投影等。

表3.5 用hitTest判断触摸图层override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) { // 获得相对于主视图的触摸位置 var point = (touches as NSSet).anyObject()!.locationInView(self.view) // 获得触碰图层 let layer = self.layerView.layer.hitTest // 用hitTest获得图层 if (layer == self.blueLayer) { UIAlertView(title: "点击蓝色视图", message: "检测到你点击了蓝色视图!", delegate: nil, cancelButtonTitle: "好的", otherButtonTitles: "取消").show() } else if (layer == self.layerView.layer) { UIAlertView(title: "点击白色视图", message: "检测到你点击了白色视图!", delegate: nil, cancelButtonTitle: "好的", otherButtonTitles: "取消").show() }}

那在个简易的例子中,大家只是实现了UILabel部分体制和布局属性,但只必要一点额外的专门的工作,大家就能够创制一个LayerLabel类来支撑完全的UILabel(你会意识互连网的开源项目中早有了如此的类)。

即便大家程序化地开创了AVPlayerLayer,大家仍将其加入三个容器视图实际不是一向加到到调节器主视图中。那样我们就可以用普通的自发性布局约束来居中图层;否则,我们只可以在道具旋转时程序化调解其职责,这是因为Core Animation不支持活动尺寸和机关布局(看第3章“图层几何”得到详细分解)。

暗中认可情状下,anchorPoint位于图层宗旨,那样不管图层在哪都会在其地方上居中。anchorPoint并不在UIView类接口中展示,那正是为什么视图的职分属性被叫作“中央”。但图层的anchorPoint能够运动,比如你能够把它内置图层frame的左上角,然后图层的剧情会向它position的右下角扩充实际不是以其为大旨。

表6.14 用ACEAGLayer绘制三角形#import "ViewController.h"#import <QuartzCore/QuartzCore.h>#import <GLKit/GLkit.h>@interface ViewController ()@property (weak, nonatomic) IBOutlet UIView *glView;@property (strong, nonatomic) EAGLContext *glContext;@property (strong, nonatomic) CAEAGLLayer *glLayer;@property (assign, nonatomic) GLuint framebuffer;@property (assign, nonatomic) GLuint colorRenderbuffer;@property (assign, nonatomic) GLint framebufferWidth;@property (assign, nonatomic) GLint framebufferHeight;@property (strong, nonatomic) GLKBaseEffect *effect;@end@implementation ViewController- setUpBuffers { // 设置帧缓冲 glGenFramebuffers(1, &_framebuffer); glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer); // 设置颜色渲染缓冲 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); // 检查结果 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { NSLog(@"Failed to make complete framebuffer object: %i", glCheckFramebufferStatus(GL_FRAMEBUFFER)); }}- tearDownBuffers { if (_framebuffer) { // 删除帧缓冲 glDeleteFramebuffers(1, &_framebuffer); _framebuffer = 0; } if (_colorRenderbuffer) { // 删除颜色渲染缓冲 glDeleteRenderbuffers(1, &_colorRenderbuffer); _colorRenderbuffer = 0; }}- drawFrame { // 绑定帧缓冲、蛇者视窗 glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer); glViewport(0, 0, _framebufferWidth, _framebufferHeight); // 绑定着色程序 [self.effect prepareToDraw]; // 清屏 glClear(GL_COLOR_BUFFER_BIT); glClearColor(0.0, 0.0, 0.0, 1.0); // 设置顶点 GLfloat vertices[] = { -0.5f, -0.5f, -1.0f, 0.0f, 0.5f, -1.0f, 0.5f, -0.5f, -1.0f, }; // 设置颜色 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, }; // 绘制三角形 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); // 显示渲染缓冲 glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderbuffer); [self.glContext presentRenderbuffer:GL_RENDERBUFFER];}- viewDidLayoutSubviews { [super viewDidLayoutSubviews]; // 判断横屏 CGSize screenSize = [[UIScreen mainScreen] applicationFrame].size; if (screenSize.width > screenSize.height) { // 设置上下文 self.glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; [EAGLContext setCurrentContext:self.glContext]; // 设置图层 self.glLayer = [CAEAGLLayer layer]; self.glLayer.frame = self.glView.bounds; [self.glView.layer addSublayer:self.glLayer]; self.glLayer.drawableProperties = @{kEAGLDrawablePropertyRetainedBacking: @NO, kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8 }; // 设置基本效果 self.effect = [[GLKBaseEffect alloc] init]; // 设置缓冲 [self setUpBuffers]; // 绘制缓冲 [self drawFrame]; }}- dealloc { [self tearDownBuffers]; [EAGLContext setCurrentContext:nil];}@end

在我们开头前,大家需求向大家项目中加多AVFoundation框架,因为它并从未默许项目模板中引进。然后看表6.15中开创轻易录像播放器的例子。图6.16显得了播音中的录制播放器。

UIView有四个注重的布局属性:framebounds以及center,对应CALayer中的frameboundsposition。为何图层使用position而视图使用center立时会讲解清楚,但它们代表着同样的值。

大家说过CATextLayerUILabel呈现质量更加好,以及部分极度的布局选项和对iOS5中富文本的支撑。但针锋相对李晖常标签,它采纳起来非凡不方便。借使大家想用三个UILabel的好用的替代品,大家相应在Interface Builder中开创我们的标签,它们也应该尽可能表现的像正规的视图。

图片 3图6.17 3d旋转、有边框和圆角的AVPlayerLayer

石英电子手表组件在Interface Builder中是那样排列的。图像视图被平放另一个容器视图中,并且禁止使用全体的电动尺寸和活动布局。那是因为机关尺寸作用于视图的frame,正如图3.2所示,frame在视图旋转时会改换,假设旋转视图的frame是机动尺寸的会招致布局失效。

CATextLayer也比UILabel渲染快。它在iOS6及此前是不解的实际,UIlabel骨子里利用WebKit来伸开文本绘制,那在您绘制多量文书时带来鲜明的性质担任。CATextLayer使用Core Text与此相同的时间显明的快速。

图片 4图6.16 AVPlayerLayer中播放中央电台频的某一静态帧

不同于UIView是严峻的二维图形,CALayer留存于三个三维空间。除了大家已经琢磨过的positionancholPoint属性,CALayer还或然有五个附加的属性,zPositionanchorPointZ,那七个都以用来图层在Z轴上地方的浮点数。

表6.5饱含了相应的代码。大家用和第5章一样的为主逻辑来放置每一个立方表面。但不一样于大家事先一直向容器视图中增添立方体表面,大家将它们放入三个CATransformLayer中来创制四个单身的立方体,然后将这一个立方放入我们的容器中。大家给那立方体表面随机涂色来便于大家不通过标签和光影得以区分它们。图6.5显得了结果。

AVPlayerLayer用来在iOS上播放录像。它是高层API如MPMoviePlayer的平底完毕,提供录制显示的尾巴部分调节。AVPlayerLayer的利用也是一定直白的:你能够选拔+playerLayerWithPlayer:艺术创立八个早已绑定的录像图层的图层,或先创立多个图层然后选拔player属性绑定AVPlayer实例。

这一章节介绍了CALayer的几何学,包涵它的frameposition以及bounds,然后我们关系了图层是存在于三个三个维度空间而非平面的学识。大家也研商了在管制图层的艺术中什么贯彻触摸事件的处理,以及iOS的'Core Animation'缺乏支撑自动尺寸以及活动布局的建制。

图片 5图6.1 贰个接纳`CAShapeLayer`绘制的简易火柴人

终极我们将执教AVPlayerLayer。纵然它不是Core Animaiton框架中的一片段,AVPlayerLayer是另一框架的例证(在此地是AVFoundation),它与Core Animation紧凑结合,提供了二个CALayer子类来呈现一个自定义内容类型。

一种缓慢解决措施是在富有图像的平底扩大额外的晶莹空间,但那会使得图像大于它们其实所必要的尺寸,它们会消耗更加的多的内部存款和储蓄器,那样不行欠雅观。

图片 6火头爆炸效果

表6.15 使用AVPlayerLayer播放视频import UIKitimport AVFoundationclass ViewController: UIViewController { @IBOutlet weak var containerView: UIView! override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 判断横屏 let screenSize = UIScreen.mainScreen().applicationFrame.size if (screenSize.width > screenSize.height) { // 获得视频URL let URL = NSBundle.mainBundle().URLForResource("Ship", withExtension: "mp4") // 创建播放器及其图层 let player = AVPlayer let playerLayer = AVPlayerLayer(player: player) // 设置播放器图层帧并加到视图中 playerLayer.frame = self.containerView.bounds self.containerView.layer.addSublayer(playerLayer) // 播放视频 player.play() } }}

在Mac OS上,CALayer有一个称作layoutManager的性质可以让您通过行使CALayoutManager这一非正式磋商和CAConstraintLayoutManager类,得以利用这一电动布局机制。但是因为某个原因,在iOS上并无法利用。[2]

假若你运维案例代码,你会小心到正是大家平素不安装contentsScale文件也不会像素化。另七个应用CATextLayer作为主图层的裨益是,contentsScale会由视图自动安装。

frame实则并不是视图或图层中的确的值;它是二个透过总结boundsposition以及transform获得的虚构值,由此会随这个值的转移而更换。而退换frame也会影响到这一个值中的某个或任何。

CAShapeLayer是叁个用矢量图而非位图绘制自个儿的图层子类。你钦点如颜色和线条粗细等品质,使用CGPath钦定想要的样子,然后CAShapeLayer会活动渲染它。当然,你也得以间接用Core Graphics在一个常见CALayer中一贯向内容里绘制路线(在第2章“主图像”中等教育授的),但用CAShapeLayer有如下一些独到之处:

图片 7图3.9 土黑视图画在戊辰革命视图前

但哪些你只想呈现一个大图层的一小部分如何做?举例,你也是有一张大图像希望客商能够四处滚动浏览,或许三个长的数额或文本表。在头名的iOS应用中,你大概会选拔UITableView或者UIScrollView,但当用独立的图层时有何一样的东西啊?

图片 8图3.4 用于组成表盘的指针的五个图像

  • 粒子的某一卓绝品质的初步值。举个例子,color品质钦定了会与contents中图像颜色相交织的水彩。在我们的例证中,我们将它设为橘色。

  • 粒子间的值区间。比如,大家项目中的emitssionRange品质被设为2π,表示粒子能够360度无死角发射。通过安装贰个相当的小值,我们会成立一个粒子的圆锥漏洞。

  • 特定值的变动时间。举个例子,在放炮项目中我们设置alphaSpeed为-0.4,那意味粒子的alpha值每秒减弱0.4,那会生出了粒子离开荒射器的渐出效果与利益。

-hitTest:主意也接受二个CGPoint;但它回到图层自个儿依旧隐含这几个点的最深层的子图层而非BOOL型。那象征你不要求像使用-containsPoint:格局一致依次手动转变、推断每一种图层是还是不是包罗触摸点。倘诺那几个点在最外层的图层边界之外,它将会再次来到nil。表3.5显示了用-hitTest:方法检验触摸图层的代码。

表6.4 使用CATextLayer的UILabel的子类——LayerLabelimport UIKitclass LayerLabel: UILabel { var layerClass: AnyClass { get { return CATextLayer.classForCoder() } } var textLayer: CATextLayer { get { return self.layer as! CATextLayer } } func setUp() { // 设置UILabel中的默认设置 self.text = self.text self.textColor = self.textColor self.font = self.font // 我们也应该从UILabel的设置的继承它们 // 但这样就太麻烦了,所以我们就只核心(hard-core)它们 self.textLayer.alignmentMode = kCAAlignmentJustified self.textLayer.wrapped = true self.layer.display() } override init(frame: CGRect) { super.init(frame: frame) self.setUp() } required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.setUp() } override func awakeFromNib() { // 当用Interface Builder创建标签时调用 self.setUp() } func setText_(text: NSString) { super.text = text as? String // 设置图层文本 self.textLayer.string = text } func setTextColor_(textColor: UIColor) { super.textColor = textColor // 设置图层文本颜色 self.textLayer.foregroundColor = textColor.CGColor } func setFont_(font: UIFont) { super.font = font // 设置图层字体 let fontName: CFStringRef = font.fontName let fontRef: CGFontRef = CGFontCreateWithFontName self.textLayer.font = fontRef self.textLayer.fontSize = font.pointSize }}
- layoutSublayersOfLayer: (CALayer *)layer;

精明的你也许想通晓到底如曾几何时候必要运用CAScrollLayer,既然您能够大致的利用CALayer与此同时调动它的boudns起源。事实上并不曾几时须求选用它。UIScrollView并不应用UIScrollLayer,它只是简短的经过一向操作图层bounds来促成的。

当使用基于图层的视图时,你能够使用UIView提供的UIViewAutoresizingMaskNSLayoutConstraint的API。但要是你想直接调整CALayer的布局,你需求手动操作。最简便易行的点子是用上面那些CALayerDelegate:方法:

CAGradientLayer用来发出二种或多样颜色之间的平整渐变。能够透过Core Graphics来将同样CAGradientLayer的法力于普通的图层主图像中,但运用CAGradientLayer的帮助和益处在于绘制是硬件加速的。

那正是说为何大家会想要更动anchorPoint?大家当然就能够将帧放在其余岗位,那改动anchorPoint只是为着制作疑心呢?为了讲明那么些怎么有用,让大家一起做个有效的事例。让大家经过运动时针、分针和秒针模拟三个挂钟。

OpenGL提供Core Animation的巩固。它是三个底层C的API接口来一向通过最小的悬空与索爱和平板电脑上的图样硬件打交道。OpenGL并未所谓的目的或图层的档案的次序;它回顾的拍卖三角形。在OpenGL中一切都以在3D空间中的三角形以及它们的颜料、纹理组成。那些方法非常灵活、有效,但利用OpenGL从头制作类似iOS客户分界面包车型客车事物要求做过多事务。

zPosition属性多数情状下并非可怜管用。在第5章中,大家将研究CATransform3d,你将学习怎么样在三维空间中移动和旋转图层。但除去变形之外,你可能发现zPosition性情的独一用途在于改造图层的呈现顺序

第2章提及的CAShapeLayer提供区别于使用CALayer cornerRadius属性外的一个可选的章程来创设带圆角的视图。就算选拔CAShapeLayer内需一些外加的劳作,但它能够让大家独立钦命每一个角的圆角。

  1. 小说中选择的时钟素材

    图片 9SecondHand.png图片 10MinuteHand.png图片 11HourHand.png图片 12ClockFace.png ↩

  2. 读者请小心原文是iOS6,译者翻译此书时已经出到iOS9,这一特征已经有所差异。 ↩

图片 13图6.6 使用CAGradientLayer的双色对角渐变

图3.8出示了一组排列在Interface Builder中的视图。正如您所见,那么些先出现在视图档案的次序中的普鲁士蓝视图被画在后出现在视图档期的顺序中海水绿视图之下。

那时就可以使用CAScrollLayer了。CAScrollLayer有一个-scrollToPoint:主意用来机关调解bounds的起点,那样图层内容看起来就好像滚动。注意,那是它所做的全部。正如先前所说,Core Animation并不会管理客户输入,所以CAScrollLayer并不担任将触摸事件转换到滚动行为,它也不会渲染滚动条也许达成其他任何iOS的一定行为如滚动回弹(当二个视图滚动出界后回弹回寻常地点)。

CALayer提供部分实用的艺术来更改不一样图层间的坐标类别:

  1. 小说中用到的斯Parker.png:

    图片 14Spark.png ↩

图片 15图3.3 改动anchorPoint对其帧的熏陶

表6.1展现贰个简约的火柴人图像的绘图代码,它应用了三个CAShapeLayer来渲染。CAShapeLayer path个性被定义为CGPathRef,但我们使用UIBezierPath辅助类来创制路线,那幸免了笔者们手动释放CGPath的烦乱。图6.1显得了结果。它并不真就是三个Rembrandt,但您掌握方法就好!

“镜头”在那边便是顶替客户的视窗。对于内置于索爱中的镜头我们并不能够做怎么样(就算它正好也本着同一方向)。

表6.12 一个简单的滚动CATiledLayer的实现import UIKitclass ViewController: UIViewController { @IBOutlet weak var scrollView: UIScrollView! override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 判断横屏 let screenSize = UIScreen.mainScreen().applicationFrame if (screenSize.width > screenSize.height) { // 增加贴图图层 let tileLayer = CATiledLayer() tileLayer.frame = CGRectMake(0, 0, 2048, 2048) tileLayer.delegate = self self.scrollView.layer.addSublayer(tileLayer) // 配置滚动视图 self.scrollView.contentSize = tileLayer.frame.size // 绘制图层 tileLayer.setNeedsDisplay() } } override func drawLayer(layer: CALayer!, inContext ctx: CGContext!) { let layer = layer as! CATiledLayer // 确定贴图坐标 let bounds = CGContextGetClipBoundingBox let x = floor(bounds.origin.x / layer.tileSize.width) let y = floor(bounds.origin.y / layer.tileSize.height) // 加载贴图 let imageName = NSString(format: "Snowman_%02i_%02i", x, y) let imagePath = NSBundle.mainBundle().pathForResource(imageName as String, ofType: "jpg")! let tileImage = UIImage(contentsOfFile: imagePath) // 绘制贴图 UIGraphicsPushContext tileImage?.drawInRect UIGraphicsPopContext() }}

更加好的消除方案是使用anchorPoint个性。让我们在-viewDidLoad主意中增进部分代码来驱动大家指针的anchorPoint舞狮。图3.7出示了正确排列的指针。

CAEmitterCell的个性平常有三类:

正如先前聊起的,视图的center个性以及图层的position品质钦点了图层相对于其父图层的anchorPoint的位置。图层的anchorPoint属性决定图层的frame相对于其position品质的岗位。你能够把anchorPoint作为随处移动图层的把手

图片 16图6.11 使用CAScrollLayer来成立凑合的轮转视图

图片 17图3.1 UIView和CALayer坐标类别

对此三个未变形的图层,图层的bounds大大小小会合作它的frame大小。frame是自动从boudns中总计的,所以改换任何性质都会更新。

大家期待在真正的行使中同样能够反射这种档期的顺序。但借使大家增添大青视图的zPosition,我们开采各样翻转了。注意,大家并不供给扩展太多;视图是最最薄的,所以纵然zPosition独有1像素的增添都会使稻草黄视图到革命视图前。更加小的值如0.1或0.0001同一会起效用,但审慎使用太小的值,因为那也许在浮点数总计时发出精度难点后形成视觉上的差别。

CAGradientLayer也有startPointendPoint本性来定义渐变方向。它们用单元坐标点名,而非点,所以图层左上角是{0, 0}右下角是{1, 1}。图6.6来得最后的渐变结果。

表面和指针用四副图像组成。为了轻巧起见,大家将用古板艺术展现并加载这个图像[1],大家选取多个单身的UIImageView实例(固然大家也可以运用正规的视图并设置它们的主图层的contents图像)。

图片 18图6.7 使用locations数组偏移至左上角的三色渐变

第2章“主图像”介绍了依据图层的图像以及在图层边界中决定它地点和缩放的有关属性。在这一章,大家将研商图层相对于父图层以及兄弟图层的职位和分寸变化。我们也会陈述怎么样管理你的图层的几何以及电动尺寸和电动大小会爆发什么样震慑。

图6.4显示了结果。(注意浅莲红的,有下划线的文字。)

其一方法会在图层bounds更动依旧图层上的-setNeedsLayout主意被调用时自动调用。那给您机遇来程序化地对您的子图层实行再度改动地方和尺寸,但并从未像UIViewautoresizingMaskconstrains属性同样提供保险图层在显示屏旋转后保持对齐的暗许行为。

并不曾强制供给提供locations数组,但只要你如此做了,你不能够不确认保证地点数和颜色数一致,不然你将获取三个空白的渐变。

我们会使用二个NSTimer来更新石英钟,并应用视图的transform脾气来旋转指针。(就算您对那天性子不熟知,不要担忧,大家将要第5章“变形”中等教育授。)表3.1体现了大家石英钟的代码。

小编们将用那么些将我们的20482048雪人图像转变来陆12个单身的256256贴图。(256*256是CATiledLayer的暗中同意大小,尽管这几个能够用tileSize属性退换。)大家的运用供给吸取输入图像文件的路径作为第多个命令行参数。大家可以在Xcode的创设立模型式中硬编码那个路子参数,但当我们未来想用一张分裂的图像时并不会很有用。因而,大家会创设那么些利用并敏锐的保存它,然后从巅峰中实行它,似乎这么:

图层就像是视图同样,有职责的承继性,每贰个图层会相对其在图层树中的父图层放置。三个图层的position是相持其父图层的bounds来说的。如若父图层移动了,全部的子图层也会移动。

// 创建路径参数let rect = CGRectMake(50, 50, 100, 100)let radii = CGSizeMakelet corners = UIRectCorner.TopRight | UIRectCorner.BottomRight | UIRectCorner.BottomLeft// 创建路径let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: radii)

frame表示图层的坐标(即它在父图层中占为己有的长空),bounds属性表示坐标(使用{0, 0}一般来讲等于图层的左上角,但那并不总是这么),而centerposition同一代表anchorPoint相持于父图层的任务。anchorPoint稍后将会解释,在那就先将它领悟成图层的基本。图3.1来得了那样属性之间的相关性。视图的frmaebounds以及center属性实际上是对应的底图层的存取器(settergetter措施)。当你手动修改视图的frame时,你其实是在修改其下CALayerframe。你不能够甩掉它的图层而单身修改视图的frame

咱们得以用有这一渠道的CAShapeLayer来创立二个混有圆角和直角的视图。借使我们想要将视图内容裁剪成那些形象,大家得以采纳大家的CAShapeLayer用作视图主图像的mask属性实际不是增进为子图层。(见第4章“视觉特效”中对图层遮罩的完好解释。)

表3.1 时钟import UIKitclass ViewController: UIViewController { @IBOutlet weak var hourHand: UIImageView! @IBOutlet weak var minuteHand: UIImageView! @IBOutlet weak var secondHand: UIImageView! var timer: NSTimer! override func viewDidLoad() { super.viewDidLoad() // 启动计时器 self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "tick", userInfo: nil, repeats: true) // 设置初始指针位置 self.tick() } func tick() { // 将时间转换成小时、分钟和秒 let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierChinese)! let units = NSCalendarUnit.CalendarUnitHour | NSCalendarUnit.CalendarUnitMinute | NSCalendarUnit.CalendarUnitSecond let components = calendar.components(units, fromDate: NSDate // 计算时针角度 let hoursAngle: CGFloat = (CGFloat(components.hour) / 12.0) * CGFloat(M_PI * 2.0) // 计算分针角度 let minsAngle: CGFloat = (CGFloat(components.minute) / 60.0) * CGFloat(M_PI * 2.0) // 计算秒针角度 let secsAngle: CGFloat = (CGFloat(components.second) / 60.0) * CGFloat(M_PI * 2.0) // 旋转指针 self.hourHand.transform = CGAffineTransformMakeRotation(hoursAngle) self.minuteHand.transform = CGAffineTransformMakeRotation(minsAngle) self.secondHand.transform = CGAffineTransformMakeRotation(secsAngle) }}

和谐完毕相应措施是相当无情的,但CAScrollLayer制止了您这一个麻烦,大约是在你图层滚动时会调治它的留存。

CALayer并无法精晓响应者链,所以它不可能直接管理触摸事件或手势识别。存在非常的多方法帮你和煦完毕触摸管理,比如:-containsPoint:-hitTest:-containsPoint:措施接收八个图层自个儿坐标连串的CGPoint,并且当点在图层本身frame中时归来YES。表3.4展现了选拔了-containsPoint:方法判定是还是不是雾灰或葡萄紫图层被点击的第1章的门类的改版代码。依次将触摸地点转变到各个图层的坐标种类来得特别困难。

在iOS 5中,Apple引进二个新的框架叫做GLKit,它通过提供叁个叫GLKViewUIView子类来移除了三个安装OpenGL绘图上下文的复杂,它帮你管理了超过半数开发银行和制图。在事先,你谐和必得利用CAEAGLLayer来做区别OpenGL绘制缓存的兼具底层配置,那是四个CALayer子类设计来直接突显OpenGL图像。

你大概注意到当调用图层的-hitTest:办法时(不幸的是那等同适应于UIView的入手管理),检查实验的种种是从严依靠图层树中的图层顺序的。我们从前谈起的zPosition质量能够影响显式的显示器上的图层顺序,但不会潜移暗化触摸管理的一一。

表6.7显示了表6.6中的对角渐变的修改版。大家明日有多少个红黄铜色三色渐变。locations数组被钦定为0.0、0.25和0.5,这会使渐变挤在视图左上角。

那几个主意令你能够某些图层中定义的点或矩形中的坐标种类转化成另叁个坐标种类。

表6.8 使用CAReplicatorLayer重复图层import UIKitclass ViewController: UIViewController { @IBOutlet weak var containerView: UIView! override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 判断横屏 let screenSize = UIScreen.mainScreen().applicationFrame if (screenSize.width > screenSize.height) { // 创建复制器图层并加入到容器视图中 let replicator = CAReplicatorLayer() replicator.frame = self.containerView.bounds self.containerView.layer.addSublayer(replicator) // 配置复制器 replicator.instanceCount = 10 // 给每个实例应用变形 var transform = CATransform3DIdentity transform = CATransform3DTranslate(transform, 0, 200, 0) transform = CATransform3DRotate(transform, CGFloat(M_PI / 5.0), 0, 0, 1) transform = CATransform3DTranslate(transform, 0, -200, 0) replicator.instanceTransform = transform // 给每个实例应用颜色偏移 replicator.instanceBlueOffset = -0.1 replicator.instanceGreenOffset = -0.1 // 创建子图层并将它放进复制器中 let layer = CALayer() layer.frame = CGRectMake(100.0, 100.0, 100.0, 100.0) layer.backgroundColor = UIColor.whiteColor().CGColor replicator.addSublayer } }}
表3.2 调整anchorPoint值后的时钟override func viewDidLoad() { super.viewDidLoad() // 调整锚点 self.secondHand.layer.anchorPoint = CGPointMake self.minuteHand.layer.anchorPoint = CGPointMake self.hourHand.layer.anchorPoint = CGPointMake // 启动计时器 self.timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: "tick", userInfo: nil, repeats: true) // 设置初始指针位置 self.tick()}

专门的工作化是每多个千头万绪组织的特点。——Catharine XC90.Stimpson

图片 19图3.2 旋转视图或图层对其frame属性的影响

Reflections通过利用CAReplicatorLayer给单唯多个复制图层应用二个负缩放因子的变形,你能够创制四个给定视图(或二个全体视图档次)的剧情镜像,创造一个实时的“镜像”效果。

像第2章中牵线的contentsRectcontentsCenter性情一样,anchorPoint采用单元坐标,那象征它的坐标是周旋于它图层的尺寸来讲。图层的左上角是{0, 0},右下角是{1, 1},因而暗中认可地点是{0.5, 0.5}anchorPoint可以由此指x或y的值小于0或超过1来使其被停放在图层边界之

表6.6 一个简单的双色对象渐变import UIKitclass ViewController: UIViewController { @IBOutlet weak var containerView: UIView! override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 判断横屏 let screenSize = UIScreen.mainScreen().applicationFrame if (screenSize.width > screenSize.height) { // 创建渐变图层并加入到容器视图中 let gradientLayer = CAGradientLayer() gradientLayer.frame = self.containerView.bounds self.containerView.layer.addSublayer(gradientLayer) // 设置渐变颜色 gradientLayer.colors = [UIColor.redColor().CGColor, UIColor.blueColor().CGColor] // 设置渐变的起点和终点 gradientLayer.startPoint = CGPointMake gradientLayer.endPoint = CGPointMake } }}

那是尽大概尝试采纳视图创设你的分界面实际不是运用管理图层的另三个好理由。

图片 20图6.5 同一个看透但分裂变形的立方体

让来那的人对几何不再一窍不通。——Plato's Academy入口的刻字

有的时候你会意识你供给绘制一个相当大的图像。三个天下第一的例子恐怕是高像素镜头拍摄制作的图纸或地表包车型地铁详细地图。iOS应用普通运维于一个内部存款和储蓄器非常轻易的装置上,所以将这么三个图像完整载入内部存款和储蓄器并非三个好主意。加载大图也是有望那些慢,简便的措施(调用UIImage -imageName:-imageWithContentsOfFile:艺术)是会令你的分界面无响应一会儿,大概至少变成动画运转不畅。

日常,图层是在它们父图层的sublayers数组中的顺序展现的。那被称作音乐大师理论,因为就好像美术师绘制一堵墙——后画的图层会覆盖先画的墙。但通过扩大图层的zPosition性格,你能够将其前移至画面,那样它就能够在情理上位居其余具备图层的前方(至少在其他有更低zPosition值的图层此前)。

变形是逐级增添的,每三个实例地点是相对于前三个实例的。这就是为什么复制品不都得了于同三个职位。图6.8显得了最终结出。

表3.3 调整zPosition来改变显示顺序import UIKitclass ViewController: UIViewController { @IBOutlet weak var greenView: UIView! @IBOutlet weak var redView: UIView! override func viewDidLoad() { super.viewDidLoad() // 将绿色视图的zPosition向镜头移近 self.greenView.layer.zPosition = 1.0 }}
表6.2 用CATextLayer实现文字标签import UIKitclass ViewController: UIViewController { @IBOutlet weak var labelView: UIView! override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 创建文字图层 let textLayer = CATextLayer() textLayer.frame = self.labelView.bounds self.labelView.layer.addSublayer(textLayer) // 设置文字属性 textLayer.foregroundColor = UIColor.blackColor().CGColor textLayer.alignmentMode = kCAAlignmentJustified textLayer.wrapped = true // 选择字体 let font = UIFont.systemFontOfSize // 设置图层字体 let fontName: CFStringRef = font.fontName let fontRef: CGFontRef = CGFontCreateWithFontName textLayer.font = fontRef textLayer.fontSize = font.pointSize // 选择文字 let text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." // 设置图层文字 textLayer.string = text }}

万般来讲,iOS中图层的position被内定为相对于父图层边界的左上角来说。而在Mac OS中则是对峙于左下角来讲。Core Animation能够透过geometryFlipped品质援助这两种情景。那是三个调整图层的多少是或不是会相对其父图层垂直翻转的BOOL值。在iOS平台上设置图层这么些值为YES壹个人着它的子图层会笔直翻转,然后会基于上面际放置而非经常境况下得下边界(那适应于它们的持有子图层,除非子图层也将geometryFlipped设置为YES)。

  • ——CAShapeLayer应用硬件加速绘制,比用Core Graphics绘制图像快比较多。
  • 一点也不慢内部存款和储蓄器——CAShapeLayer并无需像正规CALayer那么创设主图像,所以随意它多大都无需费用过多内部存款和储蓄器。
  • 不会被图层边界裁剪——CAShapeLayer能够轻便地画在分界之外。它并不会像你使用Core Graphics绘制在多少个家常CALayer中的路线同样被裁剪(正如您在第2章所见)。
  • 不会像素化——当你用变形放大学一年级个CAShapeLayer抑或用3D透视变形将其拉近镜头,它并不会像平常图层主图像同样像素化。

图片 21图3.5 在Interface Builder中放置时钟视图

程序化地开创一头手臂供给一定多的代码,所以大家会用一些更简便易行的东西来替代:在第5章中的立方体例子中,我们使用旋转镜头并不是运用容器视图的sublayerTransform来消除图层平面化的标题。那是八个不错的法子,但只对纯粹物体生效。假诺大家的景况有三个立方,我们不能够用这种方法单独地打转每三个立方。

那在活动图层地方时是足够有利的,因为它同意你运动根图层并将其子树的多少个图层作为二个总体一并活动。但奇迹你需求明白贰个图层的绝对地点或它相对于其余图层而非直接父图层的岗位。

能够在ReflectionView找到一个更完整的ReflectionView类的开源完毕,它有四个可调的渐隐效果(用CAGradientLayer和图层遮罩完成)。

图片 22图3.7 指针准确排列的表盘

表6.3 用NSAttributedString实现富文本import UIKitimport CoreTextclass ViewController: UIViewController { @IBOutlet weak var labelView: UIView! override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 创建文字图层 let textLayer = CATextLayer() textLayer.frame = self.labelView.bounds textLayer.contentsScale = UIScreen.mainScreen().scale self.labelView.layer.addSublayer(textLayer) // 设置文字属性 textLayer.alignmentMode = kCAAlignmentJustified textLayer.wrapped = true // 选择字体 let font = UIFont.systemFontOfSize // 选择文字 let text: NSString = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." // 创建属性字符串 let string = NSMutableAttributedString(string: text.description) // 将UIFont转为CTFont let fontName: CFStringRef = font.fontName let fontSize: CGFloat = font.pointSize let fontRef: CTFontRef = CTFontCreateWithName(fontName, fontSize, nil) // 设置文本属性 var attribs: Dictionary<NSObject, AnyObject> = [ kCTForegroundColorAttributeName: UIColor.blackColor().CGColor, kCTFontAttributeName: fontRef ] string.setAttributes(attribs, range: NSMakeRange(0, text.length)) attribs = [ kCTForegroundColorAttributeName: UIColor.redColor().CGColor, kCTUnderlineStyleAttributeName: NSNumber(int: CTUnderlineStyle.Single.rawValue), kCTFontAttributeName: fontRef ] string.setAttributes(attribs, range: NSMakeRange // 设置图层文本 textLayer.string = string }}

那意味一旦您转移图层的z顺序,你恐怕会开掘自身不可能检验最终面图层的触摸事件,那是因为它被另八个有更低zPosition但在图层树更前岗位的图层阻挡了。大家就要第5章深远钻探这么些题目。

客户分界面不能够仅用图像塑造。四个统一准备赏心悦指标Logo可以很好地传达开关或控件的意图,但或早或晚你必要贰个好的旧式文字标签。

图片 23图3.6 有混乱指针的表盘

表6.11呈现了一个粗略的Mac OS命令行应用的代码,它会将图像切成贴图并将它们存储为单身文件供CATiledLayer使用。

表3.4 用containsPoint:判断被触摸的图层import UIKitclass ViewController: UIViewController { @IBOutlet weak var layerView: UIView! var blueLayer: CALayer! override func viewDidLoad() { super.viewDidLoad() // 创建子图层 self.blueLayer = CALayer() self.blueLayer.frame = CGRectMake(50.0, 50.0, 100.0, 100.0) self.blueLayer.backgroundColor = UIColor.blueColor().CGColor // 将它加入到当前视图中 self.layerView.layer.addSublayer(self.blueLayer) } override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) { // 获得相对于主视图的触摸位置 var point = (touches as NSSet).anyObject()!.locationInView(self.view) // 将这个点转换为白色图层的坐标 point = self.layerView.layer.convertPoint(point, fromLayer: self.view.layer) // 使用containsPoint获得图层 if (self.layerView.layer.containsPoint { // 将点转换成蓝色图层的坐标 point = self.blueLayer.convertPoint(point, fromLayer: self.layerView.layer) if (self.blueLayer.containsPoint { UIAlertView(title: "点击蓝色视图", message: "检测到你点击了蓝色视图!", delegate: nil, cancelButtonTitle: "好的", otherButtonTitles: "取消").show() } } }}

在iOS6中,Apple扩展了对UILabel和其它UIKit文件视图的特性字符串的第一手帮助。那是贰个方便人民群众使用质量文本的管事恶性,但CATextLayer从iOS3.2引入以来都帮忙属性文本;所以如若你仍急需在你的运用中协理开始时期的iOS版本,CATextLayer是多少个给你的客户分界面增添富文本标签的简易方法,你不须求管理复杂的Core Text或然陷入使用UIWebView的分神中。

你恐怕不经常候见过UIViewAutoresizingMask常量,这一个是用来调控UIView frame在其父视图退换加大时辰怎么着立异的(日常是响应显示器从品位转向竖直只怕反过来)。

让我们用NSAttributedString修改那个例子。在iOS6及现在的版本中,我们可以用新的NSTextAttributeName常量来设置大家的字符属性,但因为练习的点在于演示这一特点在iOS5及以下版本也同样适用,我们用Core Text来代表。那表示你必要在品种中引进Core Text类型;不然,编写翻译器不会识别属性常量。

你只怕以为这能够经过在Interface Builder中调节指针图像的地方来修补,但那并不会起功效,如何图像不居中于表面它们不会不错的旋转。

表6.13 用CAEmitterLayer创建爆炸效果import UIKitclass ViewController: UIViewController { @IBOutlet weak var containerView: UIView! override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 判断横屏 let screenSize = UIScreen.mainScreen().applicationFrame if (screenSize.width > screenSize.height) { // 创建粒子发射器图层 let emitter = CAEmitterLayer() emitter.frame = self.containerView.bounds self.containerView.layer.addSublayer // 配置发射器 emitter.renderMode = kCAEmitterLayerAdditive emitter.emitterPosition = CGPointMake(emitter.frame.size.width / 2.0, emitter.frame.size.height / 2.0) // 创建粒子模板 let cell = CAEmitterCell() cell.contents = UIImage(named: "Spark.png")?.CGImage cell.birthRate = 150 cell.lifetime = 5.0 cell.color = UIColor(red: 1, green: 0.5, blue: 0.1, alpha: 1.0).CGColor cell.alphaSpeed = -0.4 cell.velocity = 50 cell.velocityRange = 50 cell.emissionRange = CGFloat * 2.0 // 向发射器添加粒子模板 emitter.emitterCells = NSArray(array: [cell]) as [AnyObject] } }}

第1章“图层树”聊起利用有主图层的视图比营造独立的图层档次更好。当中三个原因是继承者在拍卖触摸事件时会有拾贰分的头晕目眩。

这么就足以让您独自运动每一有的。转动肘可以活动小臂和手但不会活动肩膀。Core Animation图层便于在2D中贯彻这种档期的顺序结构,但无法在3D中落到实处。那是因为各种图层会将其子女平面化成叁个独门的平面(如第5章“变形”中等教育授的大同小异)。

当大家运营这些时钟应用时,它看起来有些怪。原因在于指针图像是绕着图像中央旋转的,那并非大家想要的电子原子钟指针的轴心。

表6.5 使用CATransformLayer组织3D图层层次import UIKitimport CoreTextclass ViewController: UIViewController { @IBOutlet weak var containerView: UIView! func faceWithTransform(transform: CATransform3D) -> CALayer { // 创建立方体面图层 let face = CALayer() face.frame = CGRectMake(-50, -50, 100, 100) // 添加随机颜色 let red: CGFloat = CGFloat / CGFloat let green: CGFloat = CGFloat / CGFloat let blue: CGFloat = CGFloat / CGFloat face.backgroundColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0).CGColor // 添加变形并返回 face.transform = transform return face } func cubeWithTransform(transform: CATransform3D) -> CALayer { // 创建立方体图层 let cube = CATransformLayer() // 添加面1 var ct = CATransform3DMakeTranslation cube.addSublayer(self.faceWithTransform // 添加面2 ct = CATransform3DMakeTranslation ct = CATransform3DRotate(ct, CGFloat, 0, 1, 0) cube.addSublayer(self.faceWithTransform // 添加面3 ct = CATransform3DMakeTranslation(0, -50, 0) ct = CATransform3DRotate(ct, CGFloat, 1, 0, 0) cube.addSublayer(self.faceWithTransform // 添加面4 ct = CATransform3DMakeTranslation ct = CATransform3DRotate(ct, CGFloat, 1, 0, 0) cube.addSublayer(self.faceWithTransform // 添加面5 ct = CATransform3DMakeTranslation(-50, 0, 0) ct = CATransform3DRotate(ct, CGFloat, 0, 1, 0) cube.addSublayer(self.faceWithTransform // 添加面6 ct = CATransform3DMakeTranslation(0, 0, -50) ct = CATransform3DRotate(ct, CGFloat, 0, 1, 0) cube.addSublayer(self.faceWithTransform // 在容器中居中立方体图层 let containerSize = self.containerView.bounds.size cube.position = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0) // 应用变形并返回 cube.transform = transform return cube } override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 判断横屏 let screenSize = UIScreen.mainScreen().applicationFrame if (screenSize.width > screenSize.height) { // 设置透视变形 var pt = CATransform3DIdentity pt.m34 = -1.0 / 500.0 self.containerView.layer.sublayerTransform = pt // 设置立方体1的变形并添加它 var c1t = CATransform3DIdentity c1t = CATransform3DTranslate(c1t, -100, 0, 0) let cube1 = self.cubeWithTransform self.containerView.layer.addSublayer // 设置立方体2的变形并添加它 var c2t = CATransform3DIdentity c2t = CATransform3DTranslate(c2t, 100, 0, 0) c2t = CATransform3DRotate(c2t, CGFloat, 1, 0, 0) c2t = CATransform3DRotate(c2t, CGFloat, 0, 1, 0) let cube2 = self.cubeWithTransform self.containerView.layer.addSublayer } }}
objc:- convertPoint:aPoint fromLayer:(CALayer *)layer- convertPoint:aPoint toLayer:(CALayer *)layer- convertRect:aRect fromLayer:(CALayer *)layer- convertRect:aRect toLayer:(CALayer *)layerswift:func convertPoint(_ aPoint: CGPoint, fromLayer layer: CALayer!) -> CGPointfunc convertPoint(_ aPoint: CGPoint, toLayer layer: CALayer!) -> CGPointfunc convertRect(_ aRect: CGRect, fromLayer layer: CALayer!) -> CGRectfunc convertRect(_ aRect: CGRect, toLayer layer: CALayer!) -> CGRect

不久能够不用GLKit成就那整个,但会开支大多附加工作来设置顶点心碎阴影,那是用一门叫GLSL的类C语言写的自包涵程序,将会在运作时载入图形硬件。编写GLSL代码与安装EAGLLayer并不直接想着,所以我们会用GLKBaseEffect来抽象阴影逻辑。此外,大家将用旧办法来做这全体。

图片 24图3.10 准确识别被点击的图层

您恐怕也注意到了贴图并不可能在Retina分辨率下显得。为了在器具的相应分辨率下渲染CATiledLayer,大家要求安装图层的contentsScale来适配UIScreen缩放,如:

在iOS 6中,Apple引入了自动布局编写制定。那与活动尺寸遮罩分化,但更是好用,通过点名约束结合来组成贰个系统,那一个类别是因此线性方程和不等式来定义视图的职位的分寸的。

表6.4突显的代码是创设UILabel子类LayerLabel用于选取CATextLayer并不是像正常UILabel样用慢速的-drawRect:情势来绘制文本的。LayerLabel不仅可以够用程序实例化,也能够在Inteface Builder通过丰裕普通的价签视图然后将其类设置为LayerLabel来实例化。

图片 25图6.3 设置contentsScale来适配荧屏的效应

图片 26图6.4 使用CATextLayer完结的富文本

翻译达成的机能有一些难点,还未有考察原因:

图片 27文字爆发重叠

在三个真实OpenGL应用中,大家可能想利用NSTimerCADisplayLink(见第11章“基于放大计时器的动画片”)来每秒调用-drawFrame办法六12遍,大家会将绘制和几何产目生离来防范每帧重新爆发三角形(因而大家也得以绘制另外非三角形的事物),但那应当充裕演示这一尺度了。

以此复制效果看起来也许很炫耀,但它实际上用途是哪些吗?CAReplicatorLayer对于特殊效果十一分有用,比方绘制游戏之中的枪弹轨迹,可能粒子爆炸(就算iOS5引进了CAEmitterLayer,那是更切合间接创立粒子效果的)。还会有另贰个更有效的用途:反射。

不同于UIScrollView,大家自定义的ScrollView并未达成其余边界检查。很恐怕将图层内容移出视图边缘并得以特别拖动。CAScrollLayer并不曾一样UIScrollView contentSiez的质量,由此也从不总可滚动区域的概念,那个在你滚动CAScrollLayer时确实产生的是它调度它的bounds源点到你钦定的值。它并不会调治bounds大大小小,因为它无需那样做;内容能够随意的超过边界。

同样的,CATextLayer string性情并非你想象中的一个NSString,但是id种类的。那允许你利用NSAttributedString而非NSString来钦命文本(NSAttributedString并不是NSString的子类)。属性设置是iOS用于渲染风格文本的体制。它们钦定运作风格(style runs),那是用来钦赐字符串的类型增多何种元数据譬如字体、颜色、粗体、斜体等。

表6.1 使用CAShapeLayer绘制火柴人图像import UIKitclass ViewController: UIViewController { @IBOutlet weak var containerView: UIView! override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 创建路径 let path = UIBezierPath() path.moveToPoint(CGPointMake) path.addArcWithCenter(CGPointMake, radius: 25, startAngle: 0, endAngle: CGFloat, clockwise: true) path.moveToPoint(CGPointMake) path.addLineToPoint(CGPointMake) path.addLineToPoint(CGPointMake) path.moveToPoint(CGPointMake) path.addLineToPoint(CGPointMake) path.moveToPoint(CGPointMake) path.addLineToPoint(CGPointMake) // 创建形状图层 let shapeLayer = CAShapeLayer() shapeLayer.strokeColor = UIColor.redColor().CGColor shapeLayer.fillColor = UIColor.clearColor().CGColor shapeLayer.lineWidth = 5 shapeLayer.lineJoin = kCALineJoinRound shapeLayer.lineCap = kCALineCapRound shapeLayer.path = path.CGPath // 加入到容器视图中 self.containerView.layer.addSublayer(shapeLayer) }}

CAScrollLayer并未地下的好用的脾气,尽管如此,假若你看CAScrollLayer的头文件,你会小心到它引进了一部分分类来扩大CALayer,那带有部分卓绝的方式和本性:

让大家尝试用CATextLayer彰显一些文字。表6.2体现了安装和展现CATextLayer的代码,图6.2彰显了结果。

作者们实在想要的是多个用CATextLayer作为主图层UILabel子类,然后会自行调节视图的轻重缓急,因而不用忧虑主图像被裁剪。

  • preservesDepth,它决定是还是不是二个3D粒子系统会扁平化进贰个图层或是它容器图层的任何图层在3D空间中掺杂。
  • renderMode,它决定了粒子图像怎么着视觉化地混合。你可能注意到大家的例子将它设置成kCAEmitterLayerAdditive,其职能是勾兑粒子的光影。倘若大家将其设为暗中同意值kCAEmitterLayerUnordered,结果会变得不是很特出。图片 28图6.14 禁用增添混合的火花粒子
表6.9 用CAReplicatorLayer自动绘制镜像import UIKitimport QuartzCoreclass ReflectionView: UIView { var layerClass: AnyClass { get { return CAReplicatorLayer.classForCoder() } } func setUp() { // 配置复制器 var layer: CAReplicatorLayer = CAReplicatorLayer(layer: self.layer) layer.instanceCount = 2 // 将镜像实例移到下面并且垂直翻转 var transform: CATransform3D = CATransform3DIdentity let verticalOffset: CGFloat = self.bounds.size.height + 2 transform = CATransform3DTranslate(transform, 0, verticalOffset, 0) transform = CATransform3DScale(transform, 1, -1, 0) layer.instanceTransform = transform // 减少镜像图层的透明度 layer.instanceAlphaOffset = -0.6 } override init(frame: CGRect) { super.init(frame: frame) self.setUp() } required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.setUp() } override func awakeFromNib() { super.awakeFromNib() // 当视图从nib中创建时会调用这个 self.setUp() }}

CAReplicatorLayer类用来赶快发生相似图层的相会的。它经过绘制三个或三个子图层的复制,并给每三个复制品应用不相同的变形。演示起来能够比说的接头,所以让大家来创设二个例证。

Snowman_00_00.jpgSnowman_00_01.jpgSnowman_00_02.jpg...Snowman_07_07.jpg

CAShapeLayer能够用来将另外能够用CGPath表示的模样绘制出来。这一个开着不需假设密封的,由此路线不确定是三番三遍的,所以您确实能够在二个独自的图层中绘制多少个样子。你能够调控路线的strokeColorfillColor,以及别的的品质,比如lineWidthlineCap以及lineJoin;但您在图层级只好设置那个属性三次。借使您想用区别的颜料样式绘制多个形状,你只可以为各样造型使用多个单独的图层。

CATiledLayer(分歧于多数UIKitCore Animation措施)支持三十二线程绘制。-drawLayer: inContext:方式大概同期被多线程调用,所以保障您兑现的保有绘制代码是线程安全的。

// 确定贴图坐标let bounds = CGContextGetClipBoundingBoxlet scale = UIScreen.mainScreen().scalelet x = floor(bounds.origin.x / layer.tileSize.width * scale)let y = floor(bounds.origin.y / layer.tileSize.height * scale)

因为OpenGL并不精通你的源委,它能够是一点也不慢的。通过OpenGL,你能够绘制别的你想画的,只要您提供怎么样去画必需的几何和影子逻辑。那使它产生游玩的风行选拔(那时,Core Animation的有限的优化过的开始和结果类型并不可能三回九转符合供给),但对于贰个通常的分界面来讲正是黄钟毁弃了。

很有必不可少说一下CATextLayer渲染的公文与UILabel渲染的文本在行距和字距上完全两样,那是出于它们各自使用了不一致的绘图达成(分别是Core TextWebKit)。

CATransformLayer当创设复杂的3D物体时,要是得以调整独立的要素档次将是极度造福的。比如,如若你在炮制一头胳膊:你只怕想手是手腕的儿女,手段是前臂的儿女,前臂是肘子的孩子,手肘是上臂的孩子,上臂是肩膀的男女等。

locations脾气是一组浮点数(封装成NSNumber指标)。那组数用单元坐标定义了颜色数组里种种颜色的任务,当中0.0意味渐变起头,1.0表示渐变利落。

假如您想在图层内浮现文字,你当然能够用Core Graphics利用图层委托直接向图层内容内绘制文字(那是UILabel行事原理的本来面目)。倘让你直接操作图层是可怜傻乎乎的章程,就算不是运用基于图层的视图。你将急需创制二个类来作为种种图层展现文字的图层委托,然后写下决定哪些图层要来得字符串的逻辑代码,更别讲跟踪不相同的书体、颜色等。

为了连忙利用Core Animaiton,你必要调整内你要绘制的剧情类型(矢量图形、位图、粒子、文字等)然后选用贰个确切的图层类型来呈现内容。独有部分剧情类型在Core Animaiton中有优化;所以只要您要绘制的事物不可能很好的格外任何专门的学问的图层类型,你将很难到手高质量的显示。

表6.7 使用locations数组来偏移渐变import UIKitclass ViewController: UIViewController { @IBOutlet weak var containerView: UIView! override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() // 判断横屏 let screenSize = UIScreen.mainScreen().applicationFrame if (screenSize.width > screenSize.height) { // 创建渐变图层并加入到容器视图中 let gradientLayer = CAGradientLayer() gradientLayer.frame = self.containerView.bounds self.containerView.layer.addSublayer(gradientLayer) // 设置渐变颜色 gradientLayer.colors = [UIColor.redColor().CGColor, UIColor.yellowColor().CGColor, UIColor.greenColor().CGColor] // 设置位置 gradientLayer.locations = [0.0, 0.25, 0.5] // 设置渐变的起点和终点 gradientLayer.startPoint = CGPointMake gradientLayer.endPoint = CGPointMake } }}

colors数组可以包容大肆多的您想要的水彩,所以很轻松成立就如彩虹的三番三次串渐变。默许境况下,渐变中的颜色会平分,但我们能够运用locations属性来调解间距。

CAEmitterLayer突显的像是一雨后玉兰片CAEmitterCell实例的容器,它们定义了粒子效果。你能够用模板一般创立一个或五个分裂的粒子类型,CAEmitterLayer顶住用这么些模板实例化粒子流。

翻译并从未落到实处相应效果,因而贴上原来的文章图。

表6.11 一个将图像切成贴图的终端应用#import <Foundation/Foundation.h>#import <AppKit/AppKit.h>int main(int argc, const char * argv[]) { @autoreleasepool { // 处理不正确的参数 if (argc < 2) { NSLog(@"TileCutter arguments: inputfile"); return 0; } // 输入文件 NSString *inputFile = [NSString stringWithCString:argv[1] encoding:NSUTF8StringEncoding]; // 贴图大小 CGFloat tileSize = 256; // 输出路径 NSString *outputPath = [inputFile stringByDeletingPathExtension]; // 加载图像 NSImage *image = [[NSImage alloc] initWithContentsOfFile: inputFile]; NSSize size = [image size]; NSArray *representations = [image representations]; if ([representations count]) { NSBitmapImageRep *representation = representations[0]; size.width = [representation pixelsWide]; size.height = [representation pixelsHigh]; } NSRect rect = NSMakeRect(0.0, 0.0, size.width, size.height); CGImageRef imageRef = [image CGImageForProposedRect: &rect context: NULL hints: nil]; // 计算行列 NSInteger rows = ceil(size.height / tileSize); NSInteger cols = ceil(size.width / tileSize); // 创建贴图 for (int y = 0; y < rows; ++y) { for (int x = 0; x < cols; ++x) { // 取出贴图图像 CGRect tileRect = CGRectMake(x * tileSize, y * tileSize, tileSize, tileSize); CGImageRef tileImage = CGImageCreateWithImageInRect(imageRef, tileRect); // 转换成jpeg数据 NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithCGImage:tileImage]; NSData *data = [imageRep representationUsingType: NSJPEGFileType properties: nil]; CGImageRelease(tileImage); // 存储文件 NSString *path = [outputPath stringByAppendingFormat: @"_%02i_%02i.jp", x, y]; [data writeToFile: path atomically: NO]; } } } return 0;}

CATransformLayer缓慢解决了这一问题。CATransformLayer差异于一般的CALayer,它无法呈现自己的剧情;它的存在只是为了管住一个得以行使于其子图层的变形。CATransformLayer并不会平面化其子图层,所以能够用来布局3D结构的等级次序,就如大家的双手的例证同样。

正如作者辈在第1层“图层树”中研究的,各个UIView后都有一个CALayer实例。那几个图层是由视图自动创制并管制的,所以大家怎么用三个不一品类的图层来取代?一旦图层创设大家就不可能替换它,但假若大家子类化UIView,大家得以重写+layerClass艺术来在开立时重返多少个不一的图层子类。UIView在初叶化时调用+layerClass主意,然后用它回到的图层作为主图像。

在iOS5中,Apple引进了贰个新的CALayer的子类叫CAEmitterLayerCAEmitterLayer是叁个被用来创立实时粒子动画如烟、火、雨等的高质量粒子引擎。

CAEmitterLayer质量自身决定了整整粒子系统的职位和基本造型。一些性子如birthRatelifetimevelocity复制值定义于CAEmitterCell上。它们疑似乘数,因而你可以用一个值加速或增加全数粒子系统。其余值得关怀的属性有:

那个动用特别基础,但足以很轻易地增加成可帮助额外参数的版本,举例贴图大小,可能导出非JPEG格式的图像。运维结果是爆发六14个新图像,名称如下:

图片 29图6.15 用OpenGL在CAEAGLLayer中渲染的轻便三角形

笔者们能够再三再四UILabel然后加多CATextLayer作为子图层不分厚薄写其出示文本的点子,但大家仍有由UILabel -drawRect:办法创造的空的主图像。因为CALayer不援救电动尺寸和活动布局,子图层并不会自行跟踪视图的bounds大小,所以每当视图大小改造时我们须要手动更新子图层的边际。

您恐怕从它们的名字中估算那些点子给每种CALayer实例增多滚动方法,但事实上它们只是CAScrollLayer中的图层的实用方法。scrollPoint:办法升高寻觅图层树来查找第叁个可用的CAScrollLayer,然后滚动它使的钦定点可见。scrollRectToVisible:办法对矩形做同样的业务。visibleRect性格决定CAScrollLayer当今可知的图层部分。

注意图层在再一次是什么更动的:那是用instanceBlueOffsetinstanceGreenOffset品质达成的。通过每趟重复时减弱蓝和绿的色块,大家使图层偏移成铁红。

在大家初阶前,你要求在类型中增添GLKitOpenGLES框架。接下来您应当完毕表6.14的代码,它利用OpenGL ES 2.0绘图上下文做了差不离最少的GAEAGLLayer设置,然后渲染了一个花团锦簇三角形。

当聊到iOS上的高品质图像时,最后要说的是OpenGL。它也可能是最后的点子,至少对非游戏应用是这样的,因为比较Core AnimationUIKit框架,它不是一般的复杂性。

每一个CAEmitterCell疑似三个CALayer:它有contents质量可用CGImage安装,同样也许有数不尽用来调节粒子样式和行事的铺排属性。我们不会详细描述每一个属性,但它们在CAEmitterCell头文件里都有详尽表达。

在iOS上可快速绘制的图像尺寸是有上限的。所以在显示器上出示的图像最后会转移为OpenGL纹理,OpenGL有个最大的纹路,OpenGL有一个最大的纹理大小(平常是20482048或40964096,那取决设备模型)。倘让你品味呈现一张大于最大纹理大小的图像,纵然那张图像已经存在RAM中,你仍看望到有的不佳的表现,因为Core Aniamtion被迫选用CPU并非越来越快的GPU来加载图像。(看第12章“速度微调”和第13章“高效绘图”来获取关于软件绘制和硬件绘制的区分。)

表6.10 使用CAScrollLayer实现滚动视图import UIKitclass ScrollView: UIView { var layerClass: AnyClass { get { return CAScrollLayer.classForCoder() } } func setUp() { // 允许裁剪 self.layer.masksToBounds = true // 添加拖动动作识别 let recognizer = UIPanGestureRecognizer(target: self, action: "pan:") self.addGestureRecognizer(recognizer) } override init(frame: CGRect) { super.init(frame: frame) self.setUp() } required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.setUp() } override func awakeFromNib() { super.awakeFromNib() self.setUp() } func pan(recognizer: UIPanGestureRecognizer) { // 通过当前bounds源点减去pan手势位移量获得偏移值 var offset = self.bounds.origin offset.x -= recognizer.translationInView.x offset.y -= recognizer.translationInView.y // 滚动图层 let layer = self.layer as! CAScrollLayer layer.scrollToPoint // 重置pan手势位移 recognizer.setTranslation(CGPointZero, inView: self) }}

咱俩将从贰个简易的红蓝对角渐变例子初叶。渐变颜色使用colors质量钦赐,那是三个数组。colors数组容纳CGColorRef系列的数目(那不是NSObject的派生),所以大家需求运用第2章看见的桥本领来使编写翻译器顺遂施行。

tileLayer.contentsScale = UIScreen.mainScreen().scale

让大家用CAScrollLayer来创立贰个百般基础的UIScrollView代替品。大家将创立三个以CAScrollLayer作为主图层的自定义UIView,然后采取UIPanGestureRecognizer来兑现触摸管理。代码映未来表6.10。图6.11来得ScrollView被用来在二个胜出自身帧大小的UIImageView中随处拖动。

那般就一挥而就了像素化难题。

图片 30图6.2 使用CATextLayer完毕的纯文本

侥幸的是,那不是必得的。Core Animation提供了贰个叫CATextLayerCALayer子类,它包罗了UILabel中具备图层组成的字符串绘制天性,还增加了部分非凡的天性。

你将相当少须求手动设置三个CAEAGLLayer(而是用GLKView),但让大家花点时间驾驭一下旧时的指标。非常的,我们将设置三个OpenGL ES 2.0上下文,那是兼备今世iOS设备的科班。

到近些日子甘休,大家接纳过CALayer类,大家发现它有部分使得的图像绘制和变形技术。但Core Animation图层不止是用来图像和颜料的。这一章将叙述别样你能够用来扩大Core Animation制图才具的图层类。

图片 31使用UIScrollView滚动CATiledLayer

表6.12出示了代码,图6.12展现了结果。

有意思的是,tileSize是用像素而非点为单位的,所以增添contentsScale,大家机关平分了默许的贴图大小(它未来在荧屏上是1281二十多少个点而非256256)。因而,大家没有供给手动更新贴图大小或都为Retina分辨率提供一套单独的贴图。我们只供给轻巧的改变贴图渲染代码来适应缩放的更换:

在表6.第88中学,大家在荧屏核心成立一个小的深紫方形图层,然后用CAReplicatorLayer将其转 为13个图层的环。instanceCount属性钦定图层应该被再次多少次。instanceTransform动用一个CATransform3D(在这里,是位移并旋转使图层达到圆中的下一些)。

假若您稳重看文字,你们开采有少数意料之外;文字是像素化的。那是因为它并没有在Retina分辨率下渲染。第2章提到的contentsScale属性,那是用来决定图层渲染的分辨率。contentsScale属性默认为1.0实际不是显示器缩放因子。要是你想要Retina级其他公文,大家得用下列代码给CATextLayer设置contentsScale来合作显示屏缩放。

反差大小是例外的(取决于使用的一定字体和字符),但一般是一定小的,但当你想用符合规律标签和CATextLayer完结完全同样的典范的时候,你须要记得这点。

我们将成贰个一定大的图初步——在这一个事例中,二个2048*2048的雪人。为了从CATiledLayer中收益,大家需求将它切成多少个小图像。你能够程序化的做,但借使你想加载整张图像然后在运营时切割它,你会失掉许多CATiledLayer安排提供的加载品质优势。理想图景下,你想用预管理来代表这一操作。

让大家品尝用二个可复用的UIView子类ReflectionView来贯彻这一想方设法,它会自动发出内容的镜像。创立这一个的代码是很简短的,实际上接纳ReflectionView更简明;我们能够回顾地向Interface Builder中拖入叁个ReflectionView实例,它将会在运作时发出子视图的镜像而无需向视图调控器中增添另外运行代码。

即使你只是要协助iOS6及以上版本,基于CATextLayer的标签也许用处十分小,但经常来讲,使用+layerClasss来创制基于不一样图层类型的视图是在您选取的施用CALayer子类的一清二白可重复使用的好情势。

经过这种格局来校订缩放也意味着大家的雪人图像在Retina设备上只会渲染成十分之五轻重缓急(形成总共10241024点而非先前的20482048)。图像的品类一般不会默化潜移CATiledLayer的符合规律彰显(除了图片和地图,它们是被设计来缩放展现在分歧的百分比的),它这一点值得记住。

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

让我们试一个例证:大家将要三个圆形中发出不七只速度和折射率的粒子[1]来成立火焰爆炸效果。表6.13包含了发生爆炸的代码。你能够在图6.13来看作用。

既然如此我们有了这几个贴图图像,大家需求创设贰个iOS应用来选择它们。CATiledLayer周密协作UIScrollView,所以为了促成这一例证的指标,大家会将CATiledLayer放入UIScrollView中。除了设置图层和滚动视图边界来适应大家的总图像大小外,我们全体要求做的只剩余达成-drawLayer:inContext:方法,这将在CATiledLayer急需加载一张新的贴图时调用。

textLayer.contentsScale = UIScreen.mainScreen().scale

CATextLayer font性格并非UIFont,它是CGTypeRef。那允许你根据要求用CGFontRefCTFontRef(一个Core Text font)来钦定字体。字体大小也足以独立用fontSize属性设置,因为CTFontRefCGFontRef并不像UIFont一样有一些大小。那么些事例突显了哪些将UIFont转换成CGFontRef

图片 32图6.9 在Interface Builder中使用ReflectionView图片 33图6.10 ReflectionView自动在运营时创立镜像

-  scrollPoint:  p;-  scrollRectToVisible:  r;@property CGRect visibleRect;

当您所在转悠图像时,你会当心到当CATiledLayer加载贴图时,它们会渐入。那是CATiledLayer的暗许表现。(你也许在iOS6在此以前的Apple地图应用上看见过这一效率。)你能够利用fadeDuration来改造渐变时间或剥夺它。

翻译完成效益如下:

图片 34也许有文字重叠

CATiledLayer提供叁个解决方案,当加载大图时通过将它划分成四个小的贴片并按必要独自加载它们来消除品质难题。让大家用叁个例子测验一下。

在第2章,大家批注了图层的contentsRect品质的行使,那是在三个图层中显示一张大图像的一小部分的好的化解方法。但当你的图层有子图层时,那不是三个好情势,因为每当你想滚动可视化区域时,你必要手动重新总计并更新子图层的任务。

本文由永利皇宫463登录发布于编程,转载请注明出处:一定图层,图层几何

关键词: