iOS CALayer - 图层漫谈(三)

上一篇《iOS CALayer - 图层漫谈(二)》我们聊了CALayer几何学的一些知识,这一篇呢我们来聊一下CALayer的一些视觉效果。

CALayer的圆角、边框、阴影

其实说到圆角我相信大家平时都已经用的很多了,我们通过cornerRadius这个属性来控制图层角的曲率。

1
layer.cornerRadius = 5.f;

其实这个曲率值只会影响背景颜色,而不会影响到背景图片或子图层。所以此时超出圆角部分的子图层或内容依然会显示,只有我们将maskToBounds设置成YES的时候,超出图层圆角范围的子图层或内容才会被截取。

同样,边框也是我们平常用的比较多的效果,这里就不详细解释了直接上代码吧

1
2
layer.borderWidth = 2.f;
layer.borderColor = [UIColor redColor].CGColor; // 接收一个CGColorRef类型的值

这里要强调一下的是:边框并不会将子图层(或者是寄宿图)的形状计算进来,如果有子图层(或者是寄宿图)超出了边界,边框依然会沿着图层的边界绘制出来,这跟下面要介绍的阴影正好相反。

阴影种效果也可以说是iOS一个很常见的效果了,由于之前写过一篇关于阴影设置的文章,这里就直接请大家移步到《iOS阴影设置详解》,来了解阴影相关知识。

##CALayer的图层蒙版
其实说到图层蒙版,我更想把它叫做自定义形状裁切,但本质上它又的确不是裁切,而是将我们不关心的部分遮盖住了,所以我们叫它图层蒙版。但实际展示出的效果就像裁切那样。前面我们提到了可以通过masksToBounds进行边界裁切,通过cornerRadius来设置圆角,但这些形状都很规则,当我们想得到一些边界突兀的图层部分时,我们又该怎么办呢?我们接下来要聊的mask属性就可以实现。

mask属性本身就是一个CALayer类型,有和其他图层一样的绘制和布局属性,mask图层的颜色其实是无关紧要的,真正重要的是mask图层的轮廓,当设置了mask图层后,之后mask图层轮廓内的部分会被保留下来。
先来看一下代码(只是产生最后效果部分的代码):

1
2
3
4
5
6
7
8
9
10
11
CALayer * shapLayer = [CALayer layer];
shapLayer.frame = CGRectMake(0, 0, 100, 100);
shapLayer.contents = (__bridge id)[UIImage imageNamed:@"1.png"].CGImage;
shapLayer.contentsGravity = kCAGravityResizeAspect;

CALayer * resultLayer = [CALayer layer];
resultLayer.frame = CGRectMake(100, 230, 100, 100);
resultLayer.contents = (__bridge id)[UIImage imageNamed:@"logo.jpeg"].CGImage;
resultLayer.contentsGravity = kCAGravityResizeAspect;
resultLayer.mask = shapLayer;
[self.view.layer addSublayer:resultLayer];


我们看到我们把葡萄形状的图层作为mask图层赋值给了我们想要‘剪裁’的图层,而得到的效果就是上图中的样子。

##CALayer的拉伸过滤
我们知道一张图的显示是由固定个数像素点构成的,理想情况下屏幕上的一个点对应图的一个点,这样很完美。但是当我们拉伸或者缩放图片的时候,这种显示规则就变了,呈现出的效果可能和我们想象的也不太一样。比如说我们将一张图放大到一定程度的时候,我们看到的是一个一个方形的像素,就跟马赛克似的,虽然它原本就长这个样子,但有没有办法让图放大后看起来不那么马赛克呢?这就是接下来要聊的两个高大上的属性minificationfiltermagnificationFilter了。

这两个属性都对应了三个值:

1
2
3
kCAFilterLinear
kCAFilterNeatest
kCAFilterTrilinear

默认的值都是kCAFilterLinear,我们把上面这三个值叫做三种过滤器,而每种过滤器都对应一种算法,而其实每种算法都是为了更好的呈现图片拉伸压缩后的展示效果。

由于这三种过滤器的算法都比较底层,所以这里只给大家简单的介绍一下,至于实际应用中选择哪种过滤器,大家可以通过实际的应用场景拉伸所呈现出的效果来决定。

kCAFilterLinear过滤器:采用的是双线性滤波算法,算法通过对多个像素取样最终生成新的值,从而得到一个平滑的表现不错的拉伸。
kCAFilterTrilinear过滤器:和kCAFilterLinear很相似,相对于kCAFilterLinear的双线性滤波算法,三线性滤波算法储存了多个大小情况下的图片(也叫多重图),并进行三维取样,同时结合大图小图的存储进而得到最后的结果。
kCAFilterNeatest过滤器:是一种采用比较武断算法的过滤器,从名字上来看就是就近取样,只取最近的单像素点而不管其他颜色。这样做非常快,但最明显的效果就是会使压缩的图片效果更糟,图片放大之后也显得块状,或者是马赛克严重。总的来说对于比较小的图或者是差异特别明显,有很少斜线的大图会呈现出更好的效果。

##CALayer的组透明
我们知道UIView中有一个属性叫做alpha,是用来控制视图透明度的,CALayer也有一个同样的属性叫做opacity。这两个属性都会影响子层级,也就是说如果你给一个图层设置了透明度,那么它的子图层也会受到影响,并且会产生混合叠加效果。

其实这里涉及一个透明度混合叠加的逻辑:是将所有图层先压缩成一个整体再进行透明度调整呢?还是先进行各自的透明度调整再叠加混合呢?
答案是后者。我们这里有一个实验模型,一个白色背景的UIButton上内嵌了一个同样白色背景的UILabel

如果是第一种逻辑,其实就是将UIButtonUILable合并后,作为一个整体进行透明度的调整,展示出的效果应该是“效果一”的样子,其实这也是我们大多数人想要的效果。
效果一(期望效果)
但实际情况则是“效果二”的样子,中间部分的透明度明显跟四周的透明度不同,这也是因为透明度混合叠加造成的。
效果二(真实效果)

接下来我们通过下面这张图进一步来理解一下透明度叠加的概念:

其实每一个图层的透明度都是由图层树中,当前图层以及它的父图层、祖父图层……一直到根图层的透明度累乘得到的,就像layer1-1-1的透明度是50% * 50% * 50% = 12.5%,但这并不是它最终在屏幕上呈现出的透明度,在layer1-1-1所在的区域最终在屏幕上呈现出来的透明度是由layer1layer1-1layer1-1-1叠加得到的,也就是50% + 25% + 12.5% = 87.5%

那由于透明度叠加而导致不是我们想要的效果,我们该怎么办呢?
第一种方法:我们可以修改info.plist文件中的UIViewGroupOpacityYES来达到效果,但这个设置不好的是它会影响整个应用,整个app都可能会受到不良影响。
另一种方法:我们可以设置CALayer中一个叫做shouldRasterize的属性,将它设置为YES后,图层的混合叠加逻辑就变成我们前面提到的第一种逻辑了,首先是将当前图层和它的所有子图层融合,再对当前图层进行整体的透明度调整,这样就会呈现出一个统一的透明度,也就是上面“效果一”展示的效果。


下一篇《iOS CALayer - 图层漫谈(四)》我们将聊一聊CALayer的空间变换。

版权声明:出自Jerry LMJ的原创作品,未经作者允许不得转载。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器