为何设置了button.clipsToBounds = YES;button.layer.cornerRadius = 4;也不会触发离屏渲染

1,977 阅读4分钟

如何才会触发离屏渲染

1.圆角

开启圆角都是这两行代码button.clipsToBounds = YES;button.layer.cornerRadius = 4; 通常情况下设置圆角就会触发离屏渲染,但是设置圆角并不是100%的触发离屏渲染,比如一个button 我只是设置了背景色和字体,开启圆角设置,在这种情况下会触发离屏渲染么? 带着这个疑问让我们来探究一下代码:

//1.按钮存在背景图片
    UIButton *btn1 = [UIButton buttonWithType:UIButtonTypeCustom];
    btn1.frame = CGRectMake(100, 30, 100, 100);
    btn1.layer.cornerRadius = 50;
    [self.view addSubview:btn1];
    
    [btn1 setImage:[UIImage imageNamed:@"btn.png"] forState:UIControlStateNormal];
    btn1.clipsToBounds = YES;
    
    //2.按钮不存在背景图片
    UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
    btn2.frame = CGRectMake(100, 180, 100, 100);
    btn2.layer.cornerRadius = 50;
    btn2.backgroundColor = [UIColor blueColor];
    [self.view addSubview:btn2];
    btn2.clipsToBounds = YES;
    
    //3.UIImageView 设置了图片+背景色;
    UIImageView *img1 = [[UIImageView alloc]init];
    img1.frame = CGRectMake(100, 320, 100, 100);
    img1.backgroundColor = [UIColor blueColor];
    [self.view addSubview:img1];
    img1.layer.cornerRadius = 50;
    img1.layer.masksToBounds = YES;
    img1.image = [UIImage imageNamed:@"btn.png"];
    
    //4.UIImageView 只设置了图片,无背景色;
    UIImageView *img2 = [[UIImageView alloc]init];
    img2.frame = CGRectMake(100, 480, 100, 100);
    [self.view addSubview:img2];
    img2.layer.cornerRadius = 50;
    img2.layer.masksToBounds = YES;
    img2.image = [UIImage imageNamed:@"btn.png"];

在模拟器中运行是这样的

image.png

然后我们在debug中打开color Offscree-Renderd

image.png

image.png

打开之后我们就会发现模拟器中有些图片被标记了黄色,那这些标记黄色的图片就开启了离屏渲染,1和3开启了离屏渲染。在这四段代码中都开启了圆角设置,那为什么只有1、3是离屏渲染呢?

在iOS中CALayer由backgroundColor、contents、borderWidth&borderColor构成。

image.png

和borderWidth&borderColor生效,如果contents不为空,只有设置masksToBounds为 Yes,contents才会变成圆角,此时是圆角操作可以看作两个部分渲染,并且同时需要显示到屏幕上,就会触发离屏渲染,所以代码1、3触发了离屏渲染 圆角如何才能避免离屏渲染呢

使用带有圆角图片;

预先使用CoreGraphics为图片裁剪圆角;

使用不透明的带有四个圆角图层

2. 阴影(shadow)

其原因在于,虽然layer本身是一块矩形区域,但是阴影默认是作用在其中”非透明区域“的,而且需要显示在所有layer内容的下方,因此根据画家算法必须被渲染在先。但矛盾在于此时阴影的本体(layer和其子layer)都还没有被组合到一起,怎么可能在第一步就画出只有完成最后一步之后才能知道的形状呢?这样一来又只能另外申请一块内存,把本体内容都先画好,再根据渲染结果的形状,添加阴影到frame buffer,最后把内容画上去。

image.png

阴影如何避免离屏渲染

通过CoreAnimation的shadowPath属性,记录阴影的几何形状,那么阴影就可以先被独立渲染出来,不需要依赖layer本体,也就不再需要离屏渲染了。

 view.layer.shadowPath=[UIBezierPath pathWithCGRect:view.bounds].CGPath;

3. 组透明度(group opacit)

GroupOpacity 是指 CALayer 的allowsGroupOpacity属性,UIView 的alpha属性等同于 CALayer opacity属性。开启 GroupOpacity 后,子 layer 在视觉上的透明度的上限是其父 layer 的opacity。

从 iOS 7 以后默认全局开启了这个功能,这样做是为了让子视图与其容器视图保持同样的透明度。

GroupOpacity 开启离屏渲染的条件是:layer.opacity != 1.0并且有子 layer 或者背景图。

这个触发条件并不需要subLayer.opacity != 1.0,非常容易满足。然而在 TableView 这样的视图里设置 cell 或 cell.contentView 的alpha属性小于1并不能检测离屏渲染的黄色特征,性能上也没有明显差别。经过摸索发现:只有设置 tableView 的alpha小于1时才会触发离屏渲染,对性能无明显影响;设置 cell 的alpha属性并不会对整体的透明度产生影响,只有设置 cell.contentView 才有效。

在一般的 UIViewController 的视图下可以很容易地观察到 GroupOpacity 触发的离屏渲染,这里只能猜测 TableView 更改了这些行为。

4.遮罩(Masking)

mask是应用在layer和其所有子layer的组合之上的,而且可能带有透明度,那么其实和group opacity的原理类似,不得不在离屏渲染中完成。

5.光栅化(shouldRasterize)

光栅化是手动启动离屏渲染。 shouldRasterize = false时,离屏渲染的黄色特征仅限于自动触发离屏渲染的效果的部分;shouldRasterize = true后该部分和开启了该属性的 layer 整体都有黄色特征。

光栅化的使用建议

如果layer不需要被复用,则不需要打开;

如果layer不是静态的,需要被频繁修改,比如出于动画之中,则开启光栅华反而影响性能

离屏渲染缓存有时间限制,当超过100ms,内容没有被使用就会被丢弃,无法复用 离屏渲染缓存有空间限制,超过屏幕像素的2.5倍则失效,并无法使用

离屏渲染的利弊

优点:

提高渲染效率。比如说某种效果多次出现在屏幕上,利用离屏渲染机制进行复用。

缺点

增大了性能的损耗。

容易掉帧。

离屏渲染本来是个优化设计,但是在优化同时也牺牲了性能,具体的是否启用离屏渲染,还是需要具体的需求来判断。