iOS 和宽广的离屏渲染Say Goodbye!

运动采取优化到结尾根本仍然看FPS(页面流畅程度)性能、内存占用等方面。离屏渲染也是老生常谈的一个问题,本文侧重点在大面积导致离屏渲染的因素及解决方案。

那么为何离屏渲染会挑起性能问题?

OpenGL中,GPU屏幕渲染有二种办法: On-Screen Rendering (当前屏幕渲染)
Off-Screen Rendering (离屏渲染)
,当前屏幕渲染不需要卓绝创设新的缓存,也不需要敞开新的上下文,相对于离屏渲染性能更好。可是受当前屏幕渲染的受制因素限制(只有自己上下文、屏幕缓存有限等),当前屏幕渲染有些情况下的渲染解决不了的,就利用到离屏渲染。离屏渲染的全体过程需要切换上下文环境,先从
当前屏幕切换来离屏,等收尾后,又要将上下文环境切换回来.这也是为什么会损耗性能的原由了。

离屏渲染引发因素有
cornerRadius(设置圆角)、shadows(阴影)、masks(遮罩)、edge
antialiasing(抗锯齿)、group
opacity(不透明)、shouldRasterize(光栅化)

等,至于检测离屏渲染的工具 Instruments的Core Animation
就不多说了。本文重要介绍 安装圆角阴影 的方案。

设置圆角

正常做法:

   //只需要设置layer层的两个属性
   //设置圆角
   imageView.layer.cornerRadius = imageView.frame.size.width / 2;
   //将多余的部分切掉
   imageView.layer.masksToBounds = YES;

此间提供二种防止离屏渲染的方案

  • 1.视图上添加一个子layer到最上层,用于覆盖该视图及其子视图,设置layer的图纸为刚刚可以遮盖成所需圆角样子,并且图片颜色刚好是该视图父视图的背景颜色就达成想要的功能。
    原文地址
    ,该作者写的很好,封装了一个UIView的分类,3个API,分别是
    安装一个四角圆角,设置一个指定地方的圆角,设置一个带边框的圆角

    github地址

/**
 设置一个四角圆角

 @param radius 圆角半径
 @param color  圆角背景色
 */
- (void)xw_roundedCornerWithRadius:(CGFloat)radius cornerColor:(UIColor *)color;

/**
 设置一个普通圆角

 @param radius  圆角半径
 @param color   圆角背景色
 @param corners 圆角位置
 */
- (void)xw_roundedCornerWithRadius:(CGFloat)radius cornerColor:(UIColor *)color corners:(UIRectCorner)corners;

/**
 设置一个带边框的圆角

 @param cornerRadii 圆角半径cornerRadii
 @param color       圆角背景色
 @param corners     圆角位置
 @param borderColor 边框颜色
 @param borderWidth 边框线宽
 */
- (void)xw_roundedCornerWithCornerRadii:(CGSize)cornerRadii cornerColor:(UIColor *)color corners:(UIRectCorner)corners borderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth;

下载下来那个分类直接拖入工程就可以利用了,调用很便宜,可是使用的时候会意识,这两个API都亟待传一个参数
cornerColor (父视图的背景观),所以也致使了这一个功用的受制,即
倘若该父视图的水彩不是纯色,此时该措施就不适用了,同样
假诺父视图的颜色会变化,这实现起来的代码也不那么优雅,如下图,有点难堪,那里引出了第两种方案。

边角颜色与背景象不符

  • 2.透过修改layer.mask,首先通过贝塞尔曲线创制基于矢量的门路
    ,传递给CAShapeLayer举行渲染。路径闭环,再把绘制出的Shape赋值给layer.mask,在Mask范围之外的Layer将不被出示从而达成圆角效能。代码实现很简短,如下:

    UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(130, 330, 100, 100)];
    [btn setBackgroundColor:[UIColor colorWithRed:(226.0 / 255.0) green:(113.0 / 255.0) blue:(19.0 / 255.0) alpha:1]];
    [backgroundImageView addSubview:btn];
    //绘制曲线路径
    UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:btn.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:btn.bounds.size];
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc]init];
    //设置大小
    maskLayer.frame = btn.bounds;
    //设置图形样子
    maskLayer.path = maskPath.CGPath;
    btn.layer.mask = maskLayer;

效果图:

私家认为第二种方案更简单而且效果扩张性更强些

安装阴影

常规做法:

//阴影的颜色
self.imageView.layer.shadowColor= [UIColorblackColor].CGColor;
//阴影的透明度
self.imageView.layer.shadowOpacity=0.8f;
//阴影的圆角
self.imageView.layer.shadowRadius=4;
//阴影偏移量
self.imageView.layer.shadowOffset=CGSizeMake(0,0);

优化方案:
避免对shadowOffset直接修改,通过调用setShadowPath来提供一个CGPath给视图的Layer,向Core
Animation提供渲染的View的形制Shape,就会回落离屏渲染总结

[self.imageView.layer setShadowPath:[[UIBezierPath 
    bezierPathWithRect:myView.bounds] CGPath]];

填补:当使用阴影的视图形状发生变化时,即shadowPath并不会尾随CALayer的bounds属性举办变更,所以在layer的bounds暴发变化未来需要手动更新shadowPath才能让其适配新的bounds。现实推荐看这篇散文

关于界面流畅假设想要深层探索可以看 YYKit作者 写的稿子iOS
保持界面流畅的技巧

。该随笔从屏幕突显图像的规律,到改进的方案都有详尽介绍。

多谢各位,欢迎指教!