二、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(, , , ); 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.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是一个非正式协议。其实就是说没有CALayerDelegate@protocol可以让你在类里引用啦。你只需要调用你想调用的方法,CALayer就会帮你完成城下的。
当需要被重绘时,CALayer会请求它的代理给他一个寄宿图来显示。它通过调用下面这个方法做到的:
- (void)displayLayer:(CALayer *)layer;
趁着这个机会,如果代理想直接设置contents属性的话,他就可以这么做,不然没有别的方法可以调用了。如果代理不实现该方法,CALayer就会转而尝试调用下面这个方法:
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)cox;
在调用这个方法之前,CALayer创建了一个合适尺寸的空寄宿图(尺寸由bounds和contentsScale决定)和一个Core Graphics创建的绘制上下文环境,并为绘制寄宿图做准备,它作为参数传入。而且,在开发中,我们可以让某个CALayer对象显示地调用了-display方法。不同于UIVIew,当图层显示在屏幕上时,CALayer不会自动重绘它的内容,而是把重绘的决定权交给了开发者。
寄宿图就了解到这里,未完待续。
二、CoreAnimation之寄宿图详解的更多相关文章
- SPI总线协议及SPI时序图详解
SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接口.SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚 ...
- SPI总线协议及SPI时序图详解【转】
转自:https://www.cnblogs.com/adylee/p/5399742.html SPI,是英语Serial Peripheral Interface的缩写,顾名思义就是串行外围设备接 ...
- UML类图详解_关联关系_一对多
对于一对多的示例,可以想象一个账户可以多次申购.在申购的时候没有固定上限,下限为0,那么就可以使用容器类(container class)来搞,最常见的就是vector了. 下面我们来看一个“一对多” ...
- UML类图详解_关联关系_多对一
首先先来明确一个概念,即多重性.什么是多重性呢?多重性是指两个对象之间的链接数目,表示法是“下限...上限”,最小数据为零(0),最大数目为没有设限(*),如果仅标示一个数目级上下限相同. 实际在UM ...
- Java程序员从笨鸟到菜鸟之(一百零二)sql注入攻击详解(三)sql注入解决办法
sql注入攻击详解(二)sql注入过程详解 sql注入攻击详解(一)sql注入原理详解 我们了解了sql注入原理和sql注入过程,今天我们就来了解一下sql注入的解决办法.怎么来解决和防范sql注入, ...
- (转)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 ...
- 十图详解tensorflow数据读取机制(附代码)转知乎
十图详解tensorflow数据读取机制(附代码) - 何之源的文章 - 知乎 https://zhuanlan.zhihu.com/p/27238630
- CAS (6) —— Nginx代理模式下浏览器访问CAS服务器网络顺序图详解
CAS (6) -- Nginx代理模式下浏览器访问CAS服务器网络顺序图详解 tomcat版本: tomcat-8.0.29 jdk版本: jdk1.8.0_65 nginx版本: nginx-1. ...
- 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 ...
随机推荐
- Loogn.OrmLite映射优化记录
大家对ORM效率的争议多半在映射性能方面.自己的ORMLite也是如此,经过前段时间的折腾,已经找不出一个简单的方法再提升一下这部分的方法了.在此把优化涉及的几点记录一下. 注:用于性能测试的Code ...
- VS2010 MFC对Excel的操作
这是帮别人做项目遇到的一个问题,的那个是纠结了老长时间,本以为是一件很轻松的事... 首先,这里采用了OLE来对Excel进行操作,网上其实有大把的例子,虽然都可以运行,但是并不能满足项目要求,其实我 ...
- Android Studio使用
HelloWorld在虚拟机上正常运行,在红米1S上很抱歉,HelloWorld已停止运行.有时间解决. 在windows下感觉到Android Studio非常慢,急死人.
- mysql安装和配置
一.下载mysql mysql下载页 我用的是5.6,点击旁边的"Looking for previous GA versions?"按钮就能看到5.6版本 mysql-5.6.3 ...
- 小白搭建一个网站(DouPHP)
1)安装phpStudy_2014_setup.1413444920.exe 并启动数据库 2)将软件自带的WWW实例替换成我发的这个模板(DouPHP),网上也可以下载. 能找到更好模板的也可以不用 ...
- MySql 里的IFNULL、NULLIF和ISNULL用法区别
mysql中isnull,ifnull,nullif的用法如下: isnull(expr) 的用法:如expr 为null,那么isnull() 的返回值为 1,否则返回值为 0. mysql> ...
- Pandas-数据选取
Pandas包对数据的常用数据切片功能 目录 [] where 布尔查找 isin query loc iloc ix map与lambda contains DataFrame的索引选取 [] 只能 ...
- 【转载】使用Pandas进行数据匹配
使用Pandas进行数据匹配 本文转载自:蓝鲸的网站分析笔记 原文链接:使用Pandas进行数据匹配 目录 merge()介绍 inner模式匹配 lefg模式匹配 right模式匹配 outer模式 ...
- Shader 学习笔记
Shader "Custom/Diffuse Texture" { // Shader的开始,双引号内饰该Shader的名字 Properties { _MainTex (&quo ...
- xen下离线读取虚拟机磁盘镜像的补丁
之前在xen-3.4.2和xen-4.1.2下做过几个基于qemu模拟器的补丁,就是想着不用通过xm create(xen3下面)或xl create(xen4下面)启动虚拟机,而能直接去解析磁盘镜像 ...