离屏渲染检测

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
//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"];
}
为什么1、3会触发离屏渲染?
1 设置layer的圆角,设置了背景图片,存在两个layer ,并且进行了裁剪
3 同样的道理有两个图层,并且进行了裁剪
2只有一个图层,即使裁剪了,直接进行绘制也可以到达效果
4的图层只有一个content图层,所以也不会触发离屏渲染
离屏渲染
app进行额外的渲染与合并,数据会在offScreen Buffer组合,然后转交给FrameBuffer,显示到屏幕上
离屏渲染需要额外的存储空间,大小是屏幕像素点的2.5倍
离屏渲染会对性能有影响,但是为什么离屏渲染还存在呢?
1.比如特殊效果,要使用额外的offScreenBuffer保持中间状态,不得不使用,
2.可以提高效率,假如一个效果可能多次被屏幕使用,那么提前渲染将数据保存在offScreenBuffer,就可以达到复用目的
圆角触发离屏渲染解读
先看看圆角的显示图层:

设置圆角并不是触发圆角的主要原因,我们来看看苹果官方对cornorRadius的介绍

设置layer.cornorRadius只会设置backgroundColor和border的圆角,并不会设置content的圆角,除非设置了layer.maskToBounds为True(对应View中的ClipsToBoundss属性)
非离屏渲染时当sublayer绘制到屏幕上之后,就会将sublayer从帧缓存区中移除,从而节省空间, 但是如果需要设置圆角裁剪等,很显然,多个叠加在一起的图层无法达到效果,因为无法计算
而离屏渲染,会在offscreen Buffer缓存sublayer,最终将几个sublayer组合起来显示
iOS 圆角的处理方案
1.单图层clips
imageView.clipsToBounds = true
imageView.layer.cornorRadius = 2
2.使用CoreGraphics + 贝塞尔绘制
·(UIImage *)roundedCornerImageWithCornerRadius:(CGFloat)cornerRadius {
CGFloat w=self.size.width;
CGFloat h= self.size.height;
CGFloat scale =[UIScreen mainScreenl].scale;
// 防止圆角半径小于0,或者大于宽/高中较小值的一半。
if(cornerRadius < 6)
cornerRadius = 6;
else if(cornerRadius >MIN(w, h))
cornerRadius = MIN(w,h)/2.;
UIImage *image=nil;
CGRect imageFrame= CGRectMake(0.,0., w, h);
UIGraphicsBeginImageContextwithOptions(seLf.size, NO, scale);
[[UIBezierPath bezierPathwithRoundedRect:imageframe cornerRadius:cornerRadius] addCLipl [self drawInRect:imageFramel;
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
3.使用CoreGraphics + 贝塞尔绘制
-(void)addMaskToBounds:(CGRect)maskBounds{
CGFloatw= maskBounds.size.width;
CGFloat; h = maskBounds.size.height;
CGSize size = maskBounds.size;
CGFLoat scale =[UIScreen mainScreen].scale;
CGRect imageRect = CGRectMake(6,8,w, h);
if(self.cornerRadius < 0){
self.cornerRadius = 0;
lelse if(self.cornerRadius >MIN(w, h)){\
self.cornerRadius =MIN(w,h)/2;
l
UIImage *image = self.image;
UIGraphicsBeginImageContextwithOptions(size, NO, scale);
[[UIBezierPath bezierPathwithRoundedRect:imageRect cornerRadius:self.cornerRadiusl addClip];
[image drawInRect:imageRect];
self.roundImage = UIGraphicsGetImageFromCurrentImageContext();
self.image = self.roundImage;
UIGraphicsEndImageContext();
)
4.盖上一个透明图片
5.YYImage的处理方法
- (UIImage *)yy_imageByRoundCornerRadius:(CGFloat)radius
corners:(UIRectCorner)corners
borderWidth:(CGFloat)borderWidth
borderColor:(UIColor *)borderColor
borderLineJoin:(CGLineJoin)borderLineJoin {
if (corners != UIRectCornerAllCorners) {
UIRectCorner tmp = 0;
if (corners & UIRectCornerTopLeft) tmp |= UIRectCornerBottomLeft;
if (corners & UIRectCornerTopRight) tmp |= UIRectCornerBottomRight;
if (corners & UIRectCornerBottomLeft) tmp |= UIRectCornerTopLeft;
if (corners & UIRectCornerBottomRight) tmp |= UIRectCornerTopRight;
corners = tmp;
}
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;
}
常见触发离屏渲染的几种情况
1.使用了mask的layer(layer.mask)
2.需要进行裁剪的layer(layer.maskToBounds/view.clipsToBounds),并且有多个layer
3.设置了组透明度为YES,并且透明度不为 1 的 layer (layer.allowsGroupOpacity/
layer.opacity)
4.添加了投影的layer(layer.shadow)
5.采用了光栅化的layer (layer.shouldRasterize)
6.绘制了文字的layer (UILabel, CATextLayer, Core Text 等)
7.开启了抗锯齿
网友评论