欢迎你们关注个人公众号,我会按期分享一些我在项目中遇到问题的解决办法和一些iOS实用的技巧,现阶段主要是整理出一些基础的知识记录下来
性能
文章也会同步更新到个人博客:
ppsheep.com学习
在iOS中全部的视图都是从UIView的基类派生而来,UIView能够处理触摸事件,能够支持基于Core Graphics绘图,也可以作一些简单的动画,好比旋转、缩放或者其余一些滑动渐变的动画。可是实际上,这是苹果为咱们封装了一层,真正实现动画的实际上是一个叫作图层的玩意儿(CALayer),UIView所作的一切动画,都是苹果从CALayer封装而来。动画
CALayer类在概念上和UIView相似,也能够像View同样添加一些子layer(图片,文本等等),也可以像View同样,管理子图层的位置大小等等,而且,CALayer有一些很是重要的属性和方法,iOS中的动画就是经过这些来作动画和变换,CALayer和UIView最大的不一样就是CALayer不处理用户的交互。spa
每个UIView都有一个CALayer实例属性,UIView的职责就是建立并建立这个图层,以确保在子视图被建立时,子图层也可以被建立,子视图被添加和移除的时候,子图层也可以作相对的添加移除操做。 他们的关系是一一对应的。3d
实际上,咱们在屏幕上看到的视图或者动画,其实都是图层。UIView只是苹果为咱们封装的高级API指针
这个就有个历史缘由了,主要呢 是要在Mac上也使用CALayer,可是iOS设备的触摸和Mac的鼠标点击又不同,在Mac上,高级API就叫作NSView了,更多了,就不在这里讲了。code
通常的,咱们在处理一些简单的动画时,都用不到CALayer,既然苹果为咱们封装好了,干吗不用呢?可是若是须要再处理一些高级的动画,那UIView可能就不能知足咱们的需求了。cdn
没有暴露出来的CALayer功能:对象
咱们先来感觉一下图层blog
新建一个工程
在view中添加一个view
UIView *layerView = [[UIView alloc] initWithFrame:CGRectMake(10, 10, 200, 200)];
layerView.backgroundColor = [UIColor redColor];
layerView.center = self.view.center;
[self.view addSubview:layerView];复制代码
而后咱们想要在这个小红方框的中间添加上一个蓝色的小方框。固然,咱们确定知道这很简单,直接加上一个子view就好了,可是这样作的,就失去了咱们学习CALayer的意义。
个人想法是这样,既然layer像view同样,那咱们是否能够在layerView的layer上加上一个蓝色方框样式的layer 咱们的作法是这样
CALayer *blueLayer = [CALayer layer];
blueLayer.frame = CGRectMake(50, 50, 100, 100);
blueLayer.backgroundColor = [UIColor blueColor].CGColor;
[layerView.layer addSublayer:blueLayer];复制代码
效果就出来了 咱们来看看3D图
咱们能够看到,这样的效果,咱们只能看到一个图层,一个view 并无向layerView上添加子view
固然这里咱们只是作一个layer的介绍,并非说你以后添加视图这样添加,这样确定是错误的,咱们以前讲过,layer不能处理用户的交互,这个很重要。
可是在什么状况下,咱们须要这样来使用CALayer呢?
总的来讲呢,处理视图确定比处理图层简单多了
咱们这里建立这一个例子,只是为了来介绍,图层的树状结构,和视图的一一关联关系。
寄宿图是什么意思呢?其实呢,就是图层中包含图
在CALayer中有一个属性,叫作contents,这个属性的类型被定义为id,看上去好像这个属性可以接收任意类型的值,若是给contents赋予了任意一个类型的值,你的APP也可以编译成功,可是获得的图层确实一个空白的图层,事实上,这个contents在iOS下,是须要一个CGImage的值。
那为何这个又要写做一个id类型呢,这个又是一个历史缘由了 ,明显的这个是由于macOS的缘由,由于在macOS下,这个是接收NSImage类型。
在UIImage中 有一个CGImage属性,他返回的是一个CGImageRef(指向CGImage的指针),若是你直接把这个赋给contents,那是会编译出错的。CGImageRef是一个Core Foundation对象,并非一个cocoa对象,可是咱们能够经过bridged来进行转换,咱们来向刚刚建立的layerView的图层赋予一张图片
UIImage *image = [UIImage imageNamed:@"plane"];
layerView.layer.contents = (__bridge id _Nullable)(image.CGImage);复制代码
这样咱们就避开了UIImageView,直接向UIView的图层设置一张图片
可是咱们看到中间的图片明显被拉伸了,咱们想要展现他原有的效果,怎么作呢?
在咱们使用UIImageView时,咱们处理这种拉伸,通常是使用UIimageView的一个属性
imageView.contentModel = UIViewContentModeScaleAspectFit;复制代码
在CALayer中有一个和contentMode类似的属性,叫作contentsGravity,不一样的是他是一个NSString类型。
contentsGravity值的类型包含:
这其中的类型与contentMode都有一一对应关系的,其中kCAGravityResizeAspect至关于视图中contentMode类型的UIViewContentModeScaleAspectFit
当咱们将layerView的contentsGravity设置成kCAGravityResizeAspect
layerView.layer.contentsGravity = kCAGravityResizeAspect;复制代码
效果就不同了
contentsScale定义了寄宿图的像素尺寸和视图大小的比例,默认状况下是一个值为1.0的浮点数。那么咱们通常怎么使用这个属性呢?
这个属性实际上是为了支持高分辨率屏幕机制而出现的,他用来判断在绘制图层的时候,应该为寄宿图建立建立的空间大小和须要显示图片的拉伸度。
简单来讲,若是咱们将contentsScale设置为1.0 那么寄宿图建立出来的图片将会以每一个点一个像素来绘制图片,若是设置为2,那么将会以每一个点2个像素来绘制图片。这个就是咱们熟知的retina屏幕。
在咱们以前使用contentsGravity = kCAGravityResizeAspect这个属性时,默认是将图片等比例拉伸至适应图层大小,可是,若是咱们将contentsGravity设置成kCAGravityCenter,咱们看一下,效果会是什么样?
整个图片直接放大,将原图层盖住,这是由于kCAGravityCenter属性值,默认是不会对图片进行拉伸,因此将图片的原始大小展现了出来,这时候,咱们的contentsScale就起到了做用
layerView.layer.contentsScale = image.scale;复制代码
这时,咱们看到,如今使用了正确的图片来进行绘制
注意,当咱们使用代码来设置寄宿图时,咱们必定要手动设置contentsScale
layerView.layer.contentsScale = [UIScreen mainScreen].scale;复制代码
这样,咱们的图片在retina设备上,才会显示正常。
不知你们有没有注意到,在上面的图中,咱们的图片已经超过了图层的边界,默认状况下,在UIView中,也会绘制超过边界的内容或者子视图。
在UIView中,控制是否超出边界的属性是clipsToBounds,在CALayer中,控制的属性是masksToBounds,将他设置成yes
contentRect属性,容许咱们在图层中显示寄宿图的部分区域,这涉及到图片的显示和图片是如何拉伸的,因此比contentsGravity灵活得多,这里也会多讲一下。
虽然这个属性有带一个rect的样式,这样很容易让咱们想到bounds和frame,可是这个属性和他们的使用方法确是不同的。他使用的是单位坐标,单位坐标指定在0到1之间,是一个相对值,是相对于寄宿图的位置和大小。
默认的contentsRect是{0,0,1,1} 这样就表示寄宿图的所有区域。若是咱们指定一个区域,那么寄宿图就会被显示一部分区域
能够看到如今寄宿图是所有显示的,这时候咱们来设置一下layer的contentsRect属性
layerView.layer.contentsRect = CGRectMake(0.5, 0.5, 0.5, 0.5);复制代码
这时候,图层就会这样显示
明显咱们能够看到 这是显示的图片的右下角区域 这样显示也是咱们给定的{0.5,0.5,0.5,0.5}决定的 从图片的中点位置开始,显示半宽半高
那么咱们在作APP时,什么地方常用到这个属性呢?
咱们常常在图片拼合的时候用到这个属性,这个图片拼合概念在游戏开发中常常碰到。
说简单点,就是将多张图片打包成一张图片,而后一次性载入这一账图片,这样带来的好处就是可以在内存使用上,屏幕渲染上节省不少性能。
例子:这种使用方法,咱们常见的APP中,不少美颜的相机使用了layer添加一些效果图片到当前的显示视图上,好比添加一个什么笑脸啊,相框啊之类的。
咋一看,咱们会觉得这个和寄宿图的位置相关,其实不是的,这个属性主要是用来控制图片的拉伸,其实他是一个CGRect,他定义了一个固定的边框和一个在图层上能够拉伸的区域,改变contentsCenter的值,并不会影响寄宿图的显示,除非这个图层的大小改变了,咱们才能看获得结果
contentsCenter默认的大小是{0,0,1,1},这就意味着,若是图层的大小改变了,那么整个寄宿图都会被均匀拉伸,若是咱们改变contentsCenter这个属性,定义一个拉伸的区域,那么咱们就能看到效果了
注意
这里我画了一张图,整个一个方框表示的是一个图层,若是咱们将图层的contentsCenter设置为{0.25,0.25,0.25,0.25}那么,其实这个rect造成的一个方框就是中间的I区域,至关于整个图层的正中心,宽高各一半的位置 若是咱们如今改变了图层的大小的话,咱们定义了这样一个拉伸区域{0.25,0.25,0.25,0.25},表示的就是在横向拉伸中H区域和D区域会被拉伸,在纵向拉伸中,B区域和F区域会被拉伸,而中间的I区域则是横向纵向均会被拉伸,而其中的A、C、E、G则不会被拉伸,可能这里须要着重理解一下
到这里,基本上就将CALayer中咱们可能会常用到的一些重要属性讲解了一下