GPU屏幕渲染有两种方式:
1、On-Screen Rendering(当前屏幕渲染)
指的是GPU的渲染操作是在当前显示的屏幕缓冲区进行。
2、Off-Screen Rendering(离屏渲染)
指的是GPU在当前屏幕缓冲区以外开辟一个缓冲区进行渲染操作。
离屏渲染的代价很高体现在两个方面,1、需要创建一个新的缓冲区。2、上下文切换操作。
会引发离屏渲染的操作:
1、为图层设置遮罩(layer.mask)
2、为图层设置阴影(layer.shadow)
3、为图层设置光栅化(layer.shouldRasterize = true)
4、设置layer.masksToBounds/view.clipsToBounds属性为true
5、使用CGContext在drawRect方法中绘制会耗费更多的内存。
优化方案:
iOS 9.0之前 UIImageView和UIButton设置圆角都会触发离屏渲染。
iOS 9.0之后 UIButton设置圆角会触发离屏渲染,而UIImageView里png图片并不会触发离屏渲染了,如果设置其他阴影效果之类的还是会触发离屏渲染的。
1、UIView(不包括子类)、UILabel、UITextField、UITextView,直接使用layer.cornerRadius 一行代码就行。
2、UIImageView 推荐使用方法二
方法一:使用贝塞尔曲线和CAShapeLayer,生成一个圆角layer,作为视图的layer.mask ,该方法会触发离屏渲染
- (CAShapeLayer *)creatShapLayerBounds:(CGRect)bounds {
UIBezierPath *bezier = [UIBezierPath bezierPathWithRoundedRect:bounds cornerRadius:bounds.size.height / 2];
CAShapeLayer *layer = [[CAShapeLayer alloc] init];
layer.frame = bounds;
layer.path = bezier.CGPath;
return layer;
}
方法二:使用贝塞尔曲线绘制路径,并切除多余视图,然后用view重新绘制(drawRect),该方法不会触发离屏渲染,据说会重写drawRect方法会耗费大量内存,本人用xcode测试并未发现。
- (void)drawImageView:(UIImageView *)imageview
{
UIGraphicsBeginImageContextWithOptions(imageview.bounds.size, NO, [UIScreen mainScreen].scale);
[[UIBezierPath bezierPathWithRoundedRect:imageview.bounds cornerRadius:imageview.bounds.size.height] addClip];
[imageview drawRect:view.bounds];
imageview.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
方法三:使用贝塞尔曲线绘制路径,用CGContext重绘UIImage(drawInRect),该方法也不会触发离屏渲染,但是内存会暴增。
-
(UIImage *)drawRectWithImage:(UIImage *)image
{
CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
UIBezierPath *bezier = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:rect.size.height];
UIGraphicsBeginImageContextWithOptions(rect.size, NO, [UIScreen mainScreen].scale);
CGContextAddPath(UIGraphicsGetCurrentContext(), bezier.CGPath);
CGContextClip(UIGraphicsGetCurrentContext());
[image drawInRect:rect];
CGContextDrawPath(UIGraphicsGetCurrentContext(), kCGPathFillStroke);
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();return finalImage;
}
3、UIButton
1、如果是简单的设置背景色、添加边框,可以使用上面的方法一,用贝塞尔曲线和CAShaplayer,然后将CAShaplayer添加到button的layer层上。
2、如果是设置Image,可以使用上面的方法三,drawInRect重新绘制UIImage。
3、也可以用一个中间透明,四周跟背景色相同的图片贴在button。
4、或者图片直接让UI切好,减少客户端代码量。
3、阴影(shadow)优化
通过设置shadowPath来代替shadowOffset能大幅提高性能。
imageview.layer.shadowColor = [UIColor grayColor].CGColor;
imageview.layer.shadowOpacity = 0;
imageview.layer.shadowRadius = 2.0;
UIBezierPath *path = [UIBezierPath bezierPathWithRect:imageview.frame];
imageview.layer.shadowPath = path.CGPath;