陈斌彬的技术博客

Stay foolish,stay hungry

iOS-圆角问题(Offscreen Rendering的问题)

离屏渲染是什么?

OpenGL中,GPU屏幕渲染有以下两种方式: On-Screen Rendering 意为当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行。 Off-Screen Rendering 意为离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。

相比于当前屏幕渲染,离屏渲染的代价是很高的,主要体现在两个方面: 创建新缓冲区 要想进行离屏渲染,首先要创建一个新的缓冲区。 上下文切换 离屏渲染的整个过程,需要多次切换上下文环境:先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上有需要将上下文环境从离屏切换到当前屏幕。而上下文环境的切换是要付出很大代价的。 哪些行为会导致Offscreen rendering?

  • custom drawRect: (any, even if you simply fill the background with color)
  • CALayer corner radius
  • CALayer shadow
  • CALayer mask
  • any custom drawing using CGContext

例如你的工程里的LPTagCell.m

- (void)setupViews {
    _textLabel = [[UILabel alloc]init];
    [_textLabel setTextAlignment:NSTextAlignmentCenter];
    _textLabel.font = [UIFont systemFontOfSize:14];
    [self.contentView addSubview:_textLabel];

    self.contentView.layer.masksToBounds = YES;
    self.contentView.layer.cornerRadius = 4;
}

这种写法导致了离屏渲染。

下图中黄色的部分就是离屏渲染的地方。

img

如何解决?

圆角使用UIImageView来处理。

简单来说,底层铺一个UIImageView,然后用GraphicsContext生成一张带圆角的图。

@implementation UIImage (RoundedCorner)

- (UIImage *)yal_imageWithRoundedCornersAndSize:(CGSize)sizeToFit andCornerRadius:(CGFloat)radius
{
    CGRect rect = (CGRect){0.f, 0.f, sizeToFit};

    UIGraphicsBeginImageContextWithOptions(sizeToFit, NO, UIScreen.mainScreen.scale);
    CGContextAddPath(UIGraphicsGetCurrentContext(),
                     [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius].CGPath);
    CGContextClip(UIGraphicsGetCurrentContext());

    [self drawInRect:rect];
    UIImage *output = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    return output;
}

@end

这样,就可以避免在大量cell离屏渲染的时候拖慢fps。

当然,如果只是一两个view有圆角的需求,你大可不必这么折腾。直接设置layer.cornerRadius就可以了。

设置圆角的方法也有很多种。

1.cornerRadius

2.UIBezierPath

- (void)drawRect:(CGRect)rect {
  CGRect bounds = self.bounds;
  [[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:8.0] addClip];

  [self.image drawInRect:bounds];
}

3.maskLayer(CAShapeLayer) 相关链接:Applying Rounded Corners

Mastering UIKit performance