CALayer——寄宿图
CALayer类在概念上和UIView类似,同样也是一些被层级关系树管理的矩形块,同样也可以包含一些内容(像图片,文本或者背景色),管理子图层的位置。它们有一些方法和属性用来做动画和变换。和UIView最大的不同是CALayer不处理用户的交互。
这里有一些UIView没有暴露出来的CALayer的功能:
阴影,圆角,带颜色的边框,3D变换,非矩形范围,透明遮罩,多级非线性动画
首先,使用layer写一个简单的效果
UIView *temp = [[UIView alloc]initWithFrame:CGRectMake(50, 100, 300, 400)];
[self.view addSubview:temp];
temp.backgroundColor = [UIColor blueColor];
CALayer * layer = [CALayer layer];
layer.frame = CGRectMake(50, 50 , 20, 20);
layer.backgroundColor = [UIColor yellowColor].CGColor;
[temp.layer addSublayer:layer];
contents属性
显示完色块,也可以显示图片,运用CALayer 有一个属性叫做contents,这个属性的类型被定义为id,意味着它可以是任何类型的对象。在这种情况下,你可以给contents属性赋任何值,你的app仍然能够编译通过。但是,在实践中,如果你给contents赋的不是CGImage,那么你得到的图层将是空白的。事实上,你真正要赋值的类型应该是CGImageRef,它是一个指向CGImage结构的指针。UIImage有一个CGImage属性,它返回一个”CGImageRef”,如果你想把这个值直接赋值给CALayer的contents,那你将会得到一个编译错误,可以通过bridged关键字转换。如果要给图层的寄宿图赋值,你可以按照以下这个方法
UIImage *image = [UIImage imageNamed:@"1.png"];
temp.layer.contents = (__bridge id _Nullable)(image.CGImage);
如果感觉图片有拉伸 可以添加contentMode属性,将图片进行调整
temp.contentMode = UIViewContentModeScaleAspectFit;
contentsScale属性
contentsScale属性定义了寄宿图的像素尺寸和视图大小的比例,默认情况下它是一个值为1.0的浮点数。
前提,如果设置了contentMode属性,那么contentsScale就会没有效果,因为它已经被拉伸以适应图层的边界。如果你只是单纯地想放大图层的contents图片,你可以通过使用图层的transform和affineTransform属性来达到这个目的。
当用代码的方式来处理寄宿图的时候,一定要记住要手动的设置图层的contentsScale属性,否则,你的图片在Retina设备上就显示得不正确啦。代码如下:
temp.layer.contentsScale = [UIScreen mainScreen].scale;
maskToBounds属性
masksToBounds属性可以用来决定是否显示超出边界的内容,把它设置为YES,
contentsRect属性
CALayer的contentsRect属性允许我们在图层边框里显示寄宿图的一个子域。contentsRect不是按点来计算的,它使用了单位坐标,单位坐标指定在0到1之间,是一个相对值(像素和点就是绝对值)。所以他们是相对与寄宿图的尺寸的。默认的contentsRect是{0, 0, 1, 1},这意味着整个寄宿图默认都是可见的,如果我们指定一个小一点的矩形,图片就会被裁剪。
temp.layer.contentsRect = CGRectMake(0, 0, 0.5, 0.5);
事实上给contentsRect设置一个负数的原点或是大于{1, 1}的尺寸也是可以的。这种情况下,最外面的像素会被拉伸以填充剩下的区域。
contentsCenter属性
contentsCenter其实是一个CGRect,它定义了一个固定的边框和一个在图层上可拉伸的区域。 改变contentsCenter的值并不会影响到寄宿图的显示,除非这个图层的大小改变了,你才看得到效果。默认情况下,contentsCenter是{0, 0, 1, 1},
temp.layer.contentsCenter = CGRectMake(0.5, 0.5, 0.2, 0.2);
找到一个讲解的图片不错
这意味着我们可以随意重设尺寸,边框仍然会是连续的。他工作起来的效果和UIImage里的-resizableImageWithCapInsets: 方法效果非常类似
contentsCenter可以在xib中设置!
Custome Drawing属性
contents不是唯一的方法,也能够通过继承UIView并实现-drawRect:方法来自定义绘制。当视图在屏幕上出现的时候 -drawRect:方法就会被自动调用。-drawRect:方法里面的代码利用Core Graphics去绘制一个寄宿图,然后内容就会被缓存起来直到它需要被更新(通常是因为开发者调用了-setNeedsDisplay方法,尽管影响到表现效果的属性值被更改时,一些视图类型会被自动重绘,如bounds属性)。虽然-drawRect:方法是一个UIView方法,事实上都是底层的CALayer安排了重绘工作和保存了因此产生的图片。
CALayer有一个可选的delegate属性,实现了CALayerDelegate协议,当CALayer需要一个内容特定的信息时,就会从协议中请求。CALayerDelegate是一个非正式协议,其实就是说没有CALayerDelegate @protocol可以让你在类里面引用啦。你只需要调用你想调用的方法,CALayer会帮你做剩下的。(delegate属性被声明为id类型,所有的代理方法都是可选的)。
当需要被重绘时,CALayer会请求它的代理给他一个寄宿图来显示。它通过调用 (void)displayLayer:(CALayerCALayer )layer 做到的。趁着这个机会,如果代理想直接设置contents属性的话,它就可以这么做,不然没有别的方法可以调用了。如果代理不实现-displayLayer:方法,CALayer就会转而尝试调用 - (void)drawLayer:(CALayer )layer inContext:(CGContextRef)ctx 方法
layer.delegate = self;
[layer display];//调用方法必须写
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx
{
CGContextSetLineWidth(ctx, 10.f);
CGContextSetStrokeColorWithColor(ctx, [UIColor redColor].CGColor);
CGContextStrokeEllipseInRect(ctx, layer.bounds);
}
注意:
1.在Layer上显式地调用了-display。不同于UIView,当图层显示在屏幕上时,CALayer不会自动重绘它的内容。它把重绘的决定权交给了开发者。
2.尽管没有用masksToBounds属性,绘制的那个圆仍然沿边界被裁剪了。这是因为当你使用CALayerDelegate绘制寄宿图的时候,并没有对超出边界外的内容提供绘制支持。