iOS UIView的CALayer探索 part1

来源:互联网 发布:苹果自动拨号软件 编辑:程序博客网 时间:2024/06/05 16:37

CALayer

CALayer是UIView内部实现的细节,当然苹果为开发者提供了高级API,使开发者调用简单,间接地使动画变得很简单,但这种简单会不可避免地带来一些灵活上的缺陷,如果开发者略微想在底层作一些小改变,或者想实现一些特定的动画,但苹果并没有对应的方法,这是就需要使用Core Animation。

其实图层并不能像视图那些处理触摸事件,但能实现视图不能实现的功能:

  • 阴影、圆角、带颜色的边框
  • 3D变换
  • 非矩形范围
  • 透明遮罩
  • 多级非线性动画

CALayer contents

CALayer 的contents属性,类型:id,但它在iOS上实际是只能接受CGImage,如果直接将UIImage值赋给它,只能得到一个空白的图层。这的奇怪的表现是有Mac OS的历史原因造成。它之所以被定义为id类型,是因为在Mac OS系统上,这个属性对CGImage和NSImage类型的值都起作用。同时,在UIImage上有个属性CGImage,它返回的类型实际是CGImageRef,这个CGImageRef不是Cocoa对象,而是Core Foundation类型。但我们可以通过bridge关键字转换:

layer.contents = (__bridge id)image.CGImage;

CALayer contentGravity

在平时的开发中,我们在加载图片的时候,发现会有一些图片拉伸的现象,我们通常会通过改变contentMode的枚举,让图片能正常显示,代码如下:

imageView.contentMode = UIViewContentModeScaleAspectFit; 

但有时代码赶不上变化,有时我们会发现一些视图超出我们规定的边界,默认情况下,UIView仍然会绘制超过边界的内容,我们这时会调用ClipsToBounds属性来控制是否要显示超出边界的内容。

在CALayer中,也有与UIView对应的属性:contentGravity,contentGravity是一个NSString类型,其可选的常量值如下:

/** Layer `contentsGravity' values. **/CA_EXTERN NSString * const kCAGravityCenter    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);CA_EXTERN NSString * const kCAGravityTop    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);CA_EXTERN NSString * const kCAGravityBottom    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);CA_EXTERN NSString * const kCAGravityLeft    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);CA_EXTERN NSString * const kCAGravityRight    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);CA_EXTERN NSString * const kCAGravityTopLeft    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);CA_EXTERN NSString * const kCAGravityTopRight    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);CA_EXTERN NSString * const kCAGravityBottomLeft    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);CA_EXTERN NSString * const kCAGravityBottomRight    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);CA_EXTERN NSString * const kCAGravityResize    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);CA_EXTERN NSString * const kCAGravityResizeAspect    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);CA_EXTERN NSString * const kCAGravityResizeAspectFill    CA_AVAILABLE_STARTING (10.5, 2.0, 9.0, 2.0);

同时我们在使用CALayer的时候,同样也会遇到绘制的内容超出了我们想要的边界,这时我们要是查阅苹果的API文档,我们会发现,CALayer的属性maskToBounds可以控制是否需要裁剪掉超过边界的部分。

CALayer contentsRect

在上面我们聊到CALayer的maskToBounds属性可以剪掉超过边界的部分,但我们继续探索一下,会发现CALayer有另一个属性contentsRect也能满足我们的需求.contentsRect允许开发者在图层边框里显示contentView的一个子域,同时也涉及视图如何显示和拉伸,可以说比contentsGravity更加灵活。

但contentsRect与bounds,frame不同,它不是按点计算的,而是使用单位坐标,单位坐标指定在0-1之间,是一个相对值(像素和点就是绝对值),iOS的坐标系统:

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

contentsRect默认值是{0, 0, 1, 1},contents的内容都显示出来,但我们要是修改小一点,那内容就会被裁剪。要是我们在contentsRect中设置一个负数的原点或是大于{1,1}的尺寸时,就会发现外面的像素会被拉伸以填充剩下的区域。

在游戏引擎Cocos2D中使用了一个拼合技术显示图片,拼合技术意思是图片拼合后可以打包整合到一张大图片上一次性载入。相比多次载入,拼合技术有诸多好处:更少的内存使用,更少的加载时间,优秀的图片渲染性能等等。其实我们也可以在应用中使用同样的拼合技术,用我们在上文所聊到的CALayer的contentRect就可以实现了。

CALayer contentsCenter

contentsCenter其实是一个CGRect,这个属性定义了一个固定的边框和在图层上拉伸的区域。我们改变 contentCenter的值不会影响到图层内容的显示,除非我们改变这个图层的大小。在默认的情况下,contentCenter的默认值是{0, 0, 1, 1},要是我们通过改变contentGravity改变大小,那么图层的内容就会均匀地拉伸开。它的工作效果和UIImage里的-resizableImageWithCapInsets:相似,但contentCenter可以应用到任何图层的内容上。

CALayer anchorPoint

在UIView中的center和CALayer的position都指定了anchorPoint相对于父图层的位置。图层的anchorPoint是由position来控制frame的。换句话来说,anchorPoint位于图层的中点,图层将会以这个点为中心放置。anchorPoint属性并没有再UIView中声明,所以这也是视图的position被叫做center的原因。但是图层anchorPoint可以移动的。比如我们可以吧它置于图层frame的左上角,那么图层的内容将会向右下角的position方向移动。