寄宿图
看到这个名字可能会一脸懵逼,什么是寄宿图啊?简单的来说就是图层中包含的图。
contents属性
CALayer有一个属性叫做contents
,这个属性的类型被定义为id,意味着它可以是任何类型的对象。在这种情况下,你可以给contents
属性赋任何值。但在实践中,如果你给contents
赋的不是CGImage,那么你得到的图层将是空白的。
contents
这个奇怪的表现是由Mac OS 的历史原因造成的。它之所以被定义为id类型,是因为在Mac OS系统上,这个属性对CGImage和NSImage类型的值都起作用。如果你视图在iOS平台上将UIImage的值赋给它,只能得到一个空白的图层。
令人头疼的时还不是这个问题,事实上,你真正要赋值的类型应该是CGImageRef,它是一个指向CGImage结构的指针。UIImage有一个CGImage属性,它返回一个“CGImageRef”,如果你想把这个值直接赋值给CALayer的contents
,那你将会得到一个编译错误。因为CGImageRef并不是一个真正的Cocoa对象,而是一个Core Foundation类型。
尽管Core Foundation 类型跟Cocoa对象在运行时貌似很像,他们并不是类型兼容的,不过你可以通过bridged关键字转换。如果要给图层的寄宿图赋值,你可以按照以下方法:
layer.contents = (__bridge id)image.CGImage;
下面我们继续修改上一篇建的工程:我们这里直接把view的宿主图层的contents
属性设置成图片:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.view.backgroundColor = [UIColor grayColor];
UIView *view = [[UIView alloc]init];
view.frame = CGRectMake(80,200, 200, 200);
view.backgroundColor = [UIColor whiteColor];
[self.view addSubview:view];
UIImage *image = [UIImage imageNamed:@"tupian"];
view.layer.contents = (__bridge id)image.CGImage;
}
运行结果:
![](https://img.haomeiwen.com/i939193/3b913624e3b097bb.png)
是不是有点意思了~
contentGravity
现在感觉这个图片有些不好看啊,有些拉伸了~ 在使用UIImageView的时候遇到过同样的问题,解决的办法就是把contentMode
属性设置成更合适的值:
view.contentMode = UIViewContentModeScaleAspectFit;
这个方法基本和我们遇到的情况的解决方法已经接近了,不过UIView大多数视觉相关的属性比如contentMode
,对这些属性的操作其实是对 对应图层的操作。
CALayer与contentMode
对应的属性叫做contentsGravity
但是它是一个NSString类型,而不是像对应的UIKit部分,那里面的值是枚举。contentsGravity
可选的常量值有以下这些:
- kCAGravityCenter
- kCAGravityTop
- kCAGravityBottom
- kCAGravityLeft
- kCAGravityRight
- kCAGravityTopLeft
- kCAGravityTopRight
- kCAGravityBottomLeft
- kCAGravityBottomRight
- kCAGravityResize
- kCAGravityResizeAspect
- kCAGravityResizeAspectFill
和contentMode
一样,contentsGravity
的目的是为了决定内容在图层的边界中怎么对齐,我们将使用kCAGravityResizeAspect,它的效果等同于UIViewContentModeScaleAspectFit,同时它还能在图层中等比例拉伸以适应图层的边界:
view.layer.contentsGravity = kCAGravityResizeAspect;
contentsScale
contentsScale
属性定义了寄宿图的像素尺寸和视图大小的比例,默认情况下它是一个值为1.0的浮点数。contentsScale
的目的并不是那么明显。它并不是总会对屏幕上的寄宿图有影响。如果你尝试对我们的例子设置不同的值,你就会发现根本没任何影响。 因为contents
由于设置了contentsGravity
属性,所以它已经被拉伸以适应图层的边界。
如果你只是单纯的想放大图层的contents
图片,你可以通过使用图层的transform
和affineTransform
属性来达到这个目的。
contentsScale
属性其实属于支持高分辨率屏幕机制的一部分。它用来判断在绘制图层的时候应该为寄宿图创建的控件大小,和需要显示的图片的拉伸度。UIView有一个类似的功能但是非常少用到的contentScaleFactor
属性。
如果contentsScale
设置为1.0,将会以每个点1像素绘制图片,如果设置为2.0,则会以每个点2个像素绘制图片,这就是我们熟知的Retina屏幕。
这并不会对我们在使用kCAGravityResizeAspect时产生任何影响,因为它就是拉伸图片以适应图层而已,根本不会考虑到分辨率问题。但是如果我们把contensGravity
设置为kCAGravityCenter,那么将会有很明显的变化:
view.layer.contentsGravity = kCAGravityCenter;//kCAGravityResizeAspect;
view.layer.contentsScale = [UIScreen mainScreen].scale;
![](https://img.haomeiwen.com/i939193/b4b89da57db21316.png)
view.layer.contentsGravity = kCAGravityCenter;//kCAGravityResizeAspect;
view.layer.contentsScale = image.scale;
![](https://img.haomeiwen.com/i939193/fe0cc55a8a6d3bef.png)
当用代码的方式来处理寄宿图的时候,一定要记住要手动的设置图层contentsScale
属性。
maskToBounds
UIView有一个叫做clipsToBounds
的属性可以决定是否显示超出边界的内容,CALayer对应的属性叫做maskToBounds
,把它设置为YES。
contentsRect
CALayer的contentsRect
属性允许我们在图层边框里显示寄宿图的一个子域。这涉及到图片是如何显示和拉伸的,所以比contentsGravity
灵活多了。
和bounds
、frame
不同,contentsRect
不是按点来计算的,它使用了单位坐标,单位坐标指定在0到1之间,是一个相对值。所以他们是相对与寄宿图的尺寸的。
默认的contentsRect
是{0,0,1,1},这意味着整个寄宿图默认都是可见的,如果我们指定一个小一点的矩形,图片就会被裁剪:
view.layer.contentsRect = CGRectMake(0, 0, 0.5, 0.5);
![](https://img.haomeiwen.com/i939193/fb7b82732359d3ce.png)
事实上给contentsRect
设置一个负数的原点或是大于{1,1}的尺寸也是可以的。你可以试一下哦_(可以做拼合图:像平常一样载入我们的大图,然后把它赋值给N个独立的图层的contents
,然后设置每个图层的contentsRect
来去掉我们不想显示的部分)
contentsCenter
不要被它的名字欺骗了,这是一个CGRect,它定义了一个固定的边框和一个在图层上可拉伸的区域。改变contensCenter
的值并不会影响到寄宿图的显示,除非这个图层的大小改变了,你才能看到效果。
默认情况下,contentsCenter
是{0,0,1,1},这意味着如果大小(由contentsGravity
决定)改变了,那么寄宿图将会均匀地拉伸开。但是如果我们增加原点的值并减小尺寸。我们会在图片的周围创造一个边框。下图是contentsCenter
设置为{0.25,0.25,0.5,0.5}的效果:
![](https://img.haomeiwen.com/i939193/02610c947b409cd8.png)
这意味着我们可以随意重设尺寸,边框仍然会是连续的。他工作起来的效果和UIImage里的resizableImageWithCapInsets:方法效果非常类似,只是它可以运用任何寄宿图,甚至包括在Core Graphics运行时绘制的图形。