离屏渲染

511 阅读4分钟

本文从以下四个方面来解释离屏渲染:

1.什么是离屏渲染

2.怎么看有没有触发离屏渲染

3.什么会触发离屏渲染

4.离屏渲染通过什么方式解决

1.什么是离屏渲染

我们的视图之所以能显示在屏幕上,是通过我们的CUP和GPU协作生成的帧数据,放在帧缓存区里,然后通过视频控制器拿到帧缓存显示在我们屏幕上的,但是因为某些原因会导致数据无法直接放到帧缓存区去给我们的屏幕显示,会先开辟一块offscreen区域,进行合成后再放入帧缓存区等待渲染,这个过程我们就叫离屏渲染,有关ios屏幕渲染的整个流程,我在另一篇博客有详细的介绍juejin.cn/post/700539…

2.怎么看有没有触发离屏渲染

真机打开Debug-View Debugging-Rendering-Color OffScreen Rendered Yellow 模拟器打开Debug-Color Offscreen Rendered 显示黄色的就是触发了离屏渲染

3.什么会触发离屏渲染

3.1 光栅化

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];;

view.backgroundColor = [UIColor redColor];

view.layer.shouldRasterize = YES;

[self.view addSubview:view];

3.2 遮罩

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];

view.backgroundColor = [UIColor redColor];

[self.view addSubview:view];

CALayer *layer = [CALayer layer];

layer.frame = CGRectMake(0, 0, 50, 50);

layer.backgroundColor = [UIColor greenColor].CGColor;

view.layer.mask = layer;

3.3 阴影

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];

view.backgroundColor = [UIColor redColor];

view.layer.shadowColor = [UIColor redColor].CGColor;

view.layer.shadowOffset = CGSizeMake(20,20);

view.layer.shadowOpacity = 0.1;//这句会造成离屏渲染

[self.view addSubview:view];

3.4 组透明度

UIView *view = [[UIImageView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];

view.backgroundColor = [UIColor redColor];

[self.view addSubview:view];

view.alpha = 0.5;//alpha=1不会触发离屏渲染

view.layer.allowsGroupOpacity = YES;

//有子视图,并且子视图有背景色,会触发离屏渲染

UIView *subView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];

subView.backgroundColor = [UIColor whiteColor];

[view addSubview:subView];

3.5圆角

一个layer是由backgroundColor,contens,border三个部分组成的,只有对contents设置圆角并且设置了clickToBounds=YES或者layer.masksToBounds=YES才会触发离屏渲染

UIView

例如只设置view的背景色,没有添加子视图(没有contens),设置圆角是不会触发离屏渲染的

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];

view.backgroundColor = [UIColor redColor];

[self.view addSubview:view];

view.layer.cornerRadius = 20;

view.layer.masksToBounds = YES;

如果在view上添加一个子视图呢,这个时候background和contents都需要设置圆角,就会产生离屏渲染

UIView *subView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];

subView.backgroundColor = [UIColor whiteColor];

[view addSubview:subView];

UILabel

UIlabel设置圆角不会触发离屏渲染,因为其不能添加子视图

UIImageView

只有同时设置了image及背景色才会触发离屏渲染

这段代码是不会触发离屏渲染的

UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];

imgView.image = [UIImage imageNamed:@"squrt"];

[self.view addSubview:imgView];

imgView.layer.cornerRadius = 20;

imgView.layer.masksToBounds = YES;

UIButton

设置了文本并且设置了背景色会造成离屏渲染

设置了image并且设置了背景色会造成离屏渲染

设置了setBackgroundImage会造成离屏渲染

4.怎么避免离屏渲染

大部分的离屏渲染是无法通过代码解决的,我们只能尽量避免,而且也不是说只要有离屏渲染才会造成卡顿,大量的离屏渲染才会造成卡顿,那哪些离屏渲染我们可以解决呢

4.1 阴影

通过UIBezierPath设置阴影路径就可以避免离屏渲染

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(50, 50, 100, 100)];

view.backgroundColor = [UIColor redColor];

view.layer.shadowColor = [UIColor redColor].CGColor;

view.layer.shadowPath = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, 110, 110)].CGPath;

view.layer.shadowOpacity = 0.1;

[self.view addSubview:view];

4.2 圆角

4.2.1 我们可以只设置cornerRadius不设置clickToBounds来达到不对content做圆角来避免离屏渲染

4.2.2 我们可以通过绘制的方式来解决离屏渲染,可以参考下YYWebImage的处理方式

UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);

CGContextRef context = UIGraphicsGetCurrentContext();

CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);

CGContextScaleCTM(context, 1, -1);

CGContextTranslateCTM(context, 0, -rect.size.height);

CGFloat minSize = MIN(self.size.width, self.size.height);

if (borderWidth < minSize / 2) {

UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, borderWidth, borderWidth) byRoundingCorners:corners cornerRadii:CGSizeMake(radius, borderWidth)];

[path closePath];

CGContextSaveGState(context);

[path addClip];

CGContextDrawImage(context, rect, self.CGImage);

CGContextRestoreGState(context);

}

if (borderColor && borderWidth < minSize / 2 && borderWidth > 0) {

CGFloat strokeInset = (floor(borderWidth * self.scale) + 0.5) / self.scale;

CGRect strokeRect = CGRectInset(rect, strokeInset, strokeInset);

CGFloat strokeRadius = radius > self.scale / 2 ? radius - self.scale / 2 : 0;

UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:strokeRect byRoundingCorners:corners cornerRadii:CGSizeMake(strokeRadius, borderWidth)];

[path closePath];

path.lineWidth = borderWidth;

path.lineJoinStyle = borderLineJoin;

[borderColor setStroke];

[path stroke];

}

UIImage *image = UIGraphicsGetImageFromCurrentImageContext();

UIGraphicsEndImageContext();

return image;

4.3 我们可以通过UI给一张图覆盖到圆角外的区域,从而达到设置圆角的目的