二、CoreAnimation之寄宿图详解

  在之前的图层树中我们知道,可以使用CALayer对象创建一些有背景颜色的图层,其实使用CALayer,不仅可以利用其展示背景颜色,还可以展示图片。而这些展示内容,其实就是CALayer的寄宿图。这一节我们将来探索下CALayer寄宿图。

  在CALayer中有一个属性叫做contents,这个属性的类型为id,意味着它可以是任何类型的对象,也就意味这即使你给contents属性赋任意对象值,您的项目都可以编译通过。然而,编译通过不代表使用正确,如果您给contents赋的不是CGImage对象,您的图层将展示一片空白。那既然如此,为何还要声明为id类型而不是CGImage类型呢?原因是Mac OS的历史原因造成的。在Mac OS系统中,CALayer的contents属性可以是CGImage和NSImage对象,因此CALayer的contents属性为id类型。

  事实上,您真正需要给contents属性赋值的类型应该是CGImageRef,这是一个指向CGImage结构的指针。UIImage有一个CGImage属性,它返回一个CGImageRef指针。如果您想把这个值直接赋值给CALayer的contents属性,那您将得到一个编译错误。因为CGImageRef并不是一个真正的Cocoa对象,而是一个Core Foundation类型。

  尽管Core Foundation类型跟Cocoa对象在运行时貌似很像,但他们并不是类型兼容的,不过我们仍然可以通过桥接的方式(ARC环境下),即bridge关键字转换。如果要给图层的寄宿图赋值,可以使用以下转换方法:

UIImage *image = [UIImage imageNamed:@"img01.png"];
layer.contents = (__bridge id)image.CGImage;//注意是两个下划线

  这个时候,您可能想起了UIImageView,也是用来承载UIImage对象的,那么CALayer有没有和UIImageView的相似点呢?

  答案是有的,在UIImageView中,有一个contenMode属性,用于选择内容填充模式.相似的,在CALayer中,也存在这样一个属性,即contentGravity,它的枚举值如下

  • kCAGravityCenter
  • kCAGravityTop
  • kCAGravityBottom
  • kCAGravityLeft
  • kCAGravityRight
  • kCAGravityTopLeft
  • kCAGravityTopRight
  • kCAGravityBottomLeft
  • kCAGravityBottomRight
  • kCAGravityResize
  • kCAGravityResizeAspect
  • kCAGravityResizeAspectFill

和contentMode一样,contentGravity的目的是为了解决内容在图层中的边界的对齐模式。

layer.contentsGravity = KCAGravityResizeAspect;

contentsScale:该属性定义了寄宿图的像素尺寸和视图大小的比例,默认情况下它是一个值为1.0的浮点数。一般情况下,该属性的表现并不明显,它并不是总对屏幕上的寄宿图有影响,如果您尝试对我们的上面的layer设置不同的contentsScale值,您就会发现根本没有任何的视觉上变化。因为contents由于设置了contentsGravity属性,所以他已经被拉伸以适应图层的边界。如果您只是单纯的想要放大contents的图片,您可以通过使用图层的transform和affineTransform属性来达到这个目的,因为这种放大,并非contentsScale的作用所在。

  contentsScale属性其实属于支持高分辨率(Retina)屏幕机制的一部分,它用来判断在绘制图层的时候应该为寄宿图创建的空间打下,和需要显示的图片拉伸度(假设没有设置contentsGravity属性)。UIView有一个类似功能但是非常少用到的contentScaleFactor属性。

  如果contentsScale设置为1.0,将会以每个点1像素绘制图片,如果设置为2.0,则会以每个点2像素绘制图片,这就是我们知道的Retina屏幕。这并不会对我们在使用contentsGravity时产生任何影响,因为该属性仅仅是拉伸图片适应图层而已,根本不会考虑到分辨率问题,但是如果我们把contentsGravity设置为KCAGraviryCenter(这种模式不会拉伸图片),那将会有很明显的变化。

  没有设置时代码如下:

    CALayer *layer = [CALayer layer];

    layer.frame = CGRectMake(100, 100, 100, 100);

    layer.contents = (__bridge id)[UIImage imageNamed:@"001.jpg"].CGImage;

    [self.view.layer addSublayer:layer];

  效果图如下:

当加上下面一句时

layer.contentsGravity = kCAGravityCenter;

效果图如下

  如您所见,我们的图片不仅变大而且有像素的颗粒感。这是因为和UIImage不同,CGImage没有拉伸的概念。当我们使用UIImage去读取图片时,它读取了高质量的Retina版本的图片,但是当我们用CGImage来设置图层内容时,拉伸这个属性在转换的时候就丢失了。因此我们需要手动设置contentScale来修复这个问题。

layer.contentsScale = [UIScreen mainScreen].scale;

效果图如下:

contentsRect:CALayer的contentsRect属性允许我们在图层边框里显示寄宿图的一个子域。这涉及到图片是图和显示和拉伸的,所以要比contentsGravity灵活多了。和bounds,frame不同,contentsRect不是按点来计算的,它使用了单位坐标(值在0~1之间),是一个相对值(像素和点是绝对值)。所以他们是相对于寄宿图的尺寸的 。iOS使用了以下的坐标系统:

  • 点:在iOS和Mac os中最常见的坐标体系。点就像是虚拟的像素,也被称为逻辑像素。在标准设备上,一个点就是一个像素,但在Retina设备上,一个点就等于2*2个像素。iOS用点作为屏幕的坐标测算体系就是为了在Retina设备和普通设备上能有一致的视觉效果
  • 像素:物理像素坐标并不会用来屏幕布局,但是仍然与图片有相对关系。UIImage是一个屏幕分辨率解决方案,所以指定点来度量大小。但是一些底层的图片表示如CGImage就会使用像素,所以您要清楚在Retina屏幕和普通设备上,他们表现出来了不同的大小
  • 单位:对于与图片大小或是图层边界相关的显示,单位坐标是一个方便的度量方式,当大小改变的时候,也不需要再次调整。单位坐标在OpenGL这种纹理坐标系统中用得很多,CoreAnimation中也用到了单位坐标

默认的contentsRect是{0,0,1,1},这意味着整个寄宿图默认都是可见的。如果我们指定一个小一点的矩形,

layer.contentsRect = CGRectMake(0, 0, 0.5, 0.5);

图片就会被裁剪,如图:

而contentsRect最有趣的用法在于,它可以进行图片拼合。图片可以在屏幕上独立的变换位置,载入拼合的图片。 具体做法是创建多个CALayer对象,每个对象都添加一个寄宿图,然后将这些layer添加在一个layer上即可。

自定义寄宿图

  给contents赋值CGImage并不是唯一设置寄宿图的方法,我们也可以直接利用Core Graphics直接绘制寄宿图。能够通过继承UIView并实现-drawRect方法来自定义绘制。

  -drawRect方法没有默认的实现,因为对于UIView来说,寄宿图并不是必须的,它不在意那到底是单调的颜色背景还是图片实例。如果UIView检测到-drawRect方法被调用了,它就会为视图分配一个寄宿图,这个寄宿图的像素尺寸等于视图大小乘以contentsSale的值,这无疑加大了内存的消耗。因此,如果您不需要寄宿图,那就不要创建这个方法了,这会造成CPU和内存的浪费,这也是为什么Apple建议:如果没有自定义绘制的任务就不要在子类中写一个空的drawRect方法。

  当视图在屏幕上出现的时候,-drawRect方法就会被调用,该方法利用Core Graphics去绘制一个寄宿图,然后内容就会被缓存起来知道它需要被更新(一般是开发者调用了setNeedsDisplay方法和系统检测调用)。虽然-drawRect方法是一个UIView的方法,事实上都是底层的CALayer安排了重绘工作并保存了因此产生的图片。

  CALayer有一个可选的delegate属性。实现了CALayerDelegate协议。当CALayer需要一个内容特定的信息时,就会从协议中请求。CALayerDelegate是一个非正式协议。其实就是说没有[email protected]可以让你在类里引用啦。你只需要调用你想调用的方法,CALayer就会帮你完成城下的。

  当需要被重绘时,CALayer会请求它的代理给他一个寄宿图来显示。它通过调用下面这个方法做到的:

- (void)displayLayer:(CALayer *)layer;

  趁着这个机会,如果代理想直接设置contents属性的话,他就可以这么做,不然没有别的方法可以调用了。如果代理不实现该方法,CALayer就会转而尝试调用下面这个方法:

- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)cox;

  在调用这个方法之前,CALayer创建了一个合适尺寸的空寄宿图(尺寸由bounds和contentsScale决定)和一个Core Graphics创建的绘制上下文环境,并为绘制寄宿图做准备,它作为参数传入。而且,在开发中,我们可以让某个CALayer对象显示地调用了-display方法。不同于UIVIew,当图层显示在屏幕上时,CALayer不会自动重绘它的内容,而是把重绘的决定权交给了开发者。

  寄宿图就了解到这里,未完待续。

时间: 2024-07-31 11:59:47

二、CoreAnimation之寄宿图详解的相关文章

SPI总线协议及SPI时序图详解

SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口.SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议.SPI是一个环形总线结构,由ss(cs).sck.sdi.sdo构成,其时序其实很简单,主要是在sck的控制下,两个双向移位寄存器进行数据交换. 上升沿发送.下降沿接收.高位先发送.上升沿到来

(转)CAS (4) —— CAS浏览器SSO访问顺序图详解(CAS Web Flow Diagram by Example)

CAS (4) —— CAS浏览器SSO访问顺序图详解(CAS Web Flow Diagram by Example) tomcat版本: tomcat-8.0.29 jdk版本: jdk1.8.0_65 nginx版本: nginx-1.9.8 cas版本: cas4.1.2cas-client-3.4.1 参考来源: jasig.github.io:CAS protocol CAS (1) —— Mac下配置CAS到Tomcat(服务端) CAS (2) —— Mac下配置CAS到Tomc

Android 开源框架Universal-Image-Loader完全解析(二)--- 图片缓存策略详解

本篇文章继续为大家介绍Universal-Image-Loader这个开源的图片加载框架,介绍的是图片缓存策略方面的,如果大家对这个开源框架的使用还不了解,大家可以看看我之前写的一篇文章Android 开源框架Universal-Image-Loader完全解析(一)--- 基本介绍及使用,我们一般去加载大量的图片的时候,都会做缓存策略,缓存又分为内存缓存和硬盘缓存,我之前也写了几篇异步加载大量图片的文章,使用的内存缓存是LruCache这个类,LRU是Least Recently Used 近

JavaScript 作用域链图详解

<script type="text/javascript"> /** * 作用域链: */ var a = "a"; function hao947(){ var b = "b"; alert(a);// output a alert(b);// output b // alert(c);// output undefind function hao(){ var c = "c"; alert(a);// out

一张图详解Linux中的top命令

一张图详解Linux中的top命令及每个参数的含义:

FastDFS安装、配置、部署(二)-Tracker配置详解

1.基本配置 # is this config file disabled # false for enabled # true for disabled disabled=false # bind an address of this host # empty for bind all addresses of this host bind_addr=10.16.123.132 # the tracker server pfort port=22122 # connect timeout in

useradd思维导图详解

useradd思维导图详解 本思维导图,用来说明Linux的的用户和群组的详细关系. Xmind文件和预览图: 思维导图文件用Xmind软件打开,下载链接:useradd详解.rar 预览图: Linux笔记分享,如有错误之处,欢迎留言指正,谢谢!

CentOS7安装nagios并配置出图详解

目录 开始之前 系统环境 监控内容 所需软件包 Centos7重要变化 配置开发环境 同步时间 关闭Selinux 使用CRT上传软件包 安装邮件服务 监控主机安装 常用到的命令 安装nagios所需要的运行环境 增加用户 安装nagios 配置权限 安装插件 安装nrpe 远程主机安装 常用到的命令 配置运行环境 安装nagios-plugin 安装nrpe 启动nrpe. 监控主机安装PNP 配置开发环境 安装php4nagios (版本号为0.6) 配置pnp4nagios 图表展示 问题

Storm 系列(二)—— Storm 核心概念详解

一.Storm核心概念 1.1 Topologies(拓扑) 一个完整的 Storm 流处理程序被称为 Storm topology(拓扑).它是一个是由 Spouts 和 Bolts 通过 Stream 连接起来的有向无环图,Storm 会保持每个提交到集群的 topology 持续地运行,从而处理源源不断的数据流,直到你将主动其杀死 (kill) 为止. 1.2 Streams(流) Stream 是 Storm 中的核心概念.一个 Stream 是一个无界的.以分布式方式并行创建和处理的