CALayer是UIView内部实现的细节,当然苹果为开发者提供了高级API,使开发者调用简单,间接地使动画变得很简单,但这种简单会不可避免地带来一些灵活上的缺陷,如果开发者略微想在底层作一些小改变,或者想实现一些特定的动画,但苹果并没有对应的方法,这是就需要使用Core Animation。
其实图层并不能像视图那些处理触摸事件,但能实现视图不能实现的功能:
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;
在平时的开发中,我们在加载图片的时候,发现会有一些图片拉伸的现象,我们通常会通过改变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的maskToBounds属性可以剪掉超过边界的部分,但我们继续探索一下,会发现CALayer有另一个属性contentsRect也能满足我们的需求.contentsRect允许开发者在图层边框里显示contentView的一个子域,同时也涉及视图如何显示和拉伸,可以说比contentsGravity更加灵活。
但contentsRect与bounds,frame不同,它不是按点计算的,而是使用单位坐标,单位坐标指定在0-1之间,是一个相对值(像素和点就是绝对值),iOS的坐标系统:
contentsRect默认值是{0, 0, 1, 1},contents的内容都显示出来,但我们要是修改小一点,那内容就会被裁剪。要是我们在contentsRect中设置一个负数的原点或是大于{1,1}的尺寸时,就会发现外面的像素会被拉伸以填充剩下的区域。
在游戏引擎Cocos2D中使用了一个拼合技术显示图片,拼合技术意思是图片拼合后可以打包整合到一张大图片上一次性载入。相比多次载入,拼合技术有诸多好处:更少的内存使用,更少的加载时间,优秀的图片渲染性能等等。其实我们也可以在应用中使用同样的拼合技术,用我们在上文所聊到的CALayer的contentRect就可以实现了。
contentsCenter其实是一个CGRect,这个属性定义了一个固定的边框和在图层上拉伸的区域。我们改变 contentCenter的值不会影响到图层内容的显示,除非我们改变这个图层的大小。在默认的情况下,contentCenter的默认值是{0, 0, 1, 1},要是我们通过改变contentGravity改变大小,那么图层的内容就会均匀地拉伸开。它的工作效果和UIImage里的-resizableImageWithCapInsets:相似,但contentCenter可以应用到任何图层的内容上。
在UIView中的center和CALayer的position都指定了anchorPoint相对于父图层的位置。图层的anchorPoint是由position来控制frame的。换句话来说,anchorPoint位于图层的中点,图层将会以这个点为中心放置。anchorPoint属性并没有再UIView中声明,所以这也是视图的position被叫做center的原因。但是图层anchorPoint可以移动的。比如我们可以吧它置于图层frame的左上角,那么图层的内容将会向右下角的position方向移动。