iOS的非离屏渲染圆角

来源:互联网 发布:linux查看文件的命令 编辑:程序博客网 时间:2024/04/28 03:52

实际开发中,大多会遇到圆角或者圆形的控件的情况。通常,简便的解决方案主要是:

1.让美工做一个圆角的图片,我们直接放图片就OK。

2.就是常用的layer的那两个属性(cornerRadius , masksToBounds)。

       第一种方法不说了,第二种方法,在圆角不多的时候还可以,如果一个界面上的圆角控件很多的时候,再用它就出问题了,。就像下面这种情况的时候,滑动tableView就会明显感觉到屏幕的卡顿:

模拟器截图

究其原因,我们在用masksToBounds这个属性的时候GPU屏幕渲染才用了离屏渲染的方式。由此,我们引出了离屏渲染的概念——

离屏渲染是什么?

OpenGL中,GPU屏幕渲染有以下两种方式:

 1、On-Screen Rendering:   意思是当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区进行。 2、Off-Screen Rendering:   意思就是我们说的离屏渲染了,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。

相比于当前屏幕渲染,离屏渲染的代价是很高的,主要体现在两个方面:

一、创建新缓冲区,要想进行离屏渲染,首先要创建一个新的缓冲区。
二、上下文切换,离屏渲染的整个过程,需要多次切换上下文环境:先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上有需要将上下文环境从离屏切换到当前屏幕。而上下文环境的切换是要付出很大代价的。

会导致离屏渲染的几种情况:

  1. custom drawRect: (any, even if you simply fill the background with color)

  2. CALayer shadow

  3. CALayer mask

  4. any custom drawing using CGContext

解决圆角离屏渲染的方案:

圆角使用UIImageView装载一个圆角image来处理,简单地说就是比如一个UIView(或其子类)设置圆角,就在底层铺一个UIImageView,然后用GraphicsContext生成一张带圆角的图片放在UIImageView上。

#import"DrCorner.h"@interfaceDrCorner()@end@implementationDrCorner+ (CGFloat)ceilbyunit:(CGFloat)num unit:(double)unit {returnnum -modf(num, &unit) + unit;}+ (CGFloat)floorbyunit:(CGFloat)num unit:(double)unit {returnnum -modf(num, &unit);}+ (CGFloat)roundbyunit:(CGFloat)num unit:(double)unit {CGFloatremain =modf(num, &unit);if(remain > unit /2.0) {return[selfceilbyunit:numunit:unit];}else{return[selffloorbyunit:numunit:unit];}}+ (CGFloat)pixel:(CGFloat)num {CGFloatunit;CGFloatscale = [[UIScreenmainScreen]scale];switch((NSInteger)scale) {case1:unit =1.0/1.0;break;case2:unit =1.0/2.0;break;case3:unit =1.0/3.0;break;default:unit =0.0;break;}return[selfroundbyunit:numunit:unit];}@end- (void)dr_addCornerRadius:(CGFloat)radiusborderWidth:(CGFloat)borderWidthbackgroundColor:(UIColor*)backgroundColorborderCorlor:(UIColor*)borderColor {UIImageView*imageView = [[UIImage Viewalloc]initWithImage:[self dr_drawRectWithRoundedCornerRadius:radius borderWidth:borderWidthbackgroundColor:backgroundColorborderCorlor:borderColor]];[selfinsertSubview:imageViewatIndex:0];}- (UIImage*)dr_drawRectWithRoundedCornerRadius:(CGFloat)radiusborderWidth:(CGFloat)borderWidthbackgroundColor:(UIColor*)backgroundColorborderCorlor:(UIColor*)borderColor {CGSizesizeToFit =CGSizeMake([DrCornerpixel:self.bounds.size.width],self.bounds.size.height);CGFloathalfBorderWidth = borderWidth /2.0;UIGraphicsBeginImageContextWithOptions(sizeToFit,NO, [UIScreenmainScreen].scale);CGContextRefcontext =UIGraphicsGetCurrentContext();CGContextSetLineWidth(context, borderWidth);CGContextSetStrokeColorWithColor(context, borderColor.CGColor);CGContextSetFillColorWithColor(context, backgroundColor.CGColor);CGFloatwidth = sizeToFit.width, height = sizeToFit.height;CGContextMoveToPoint(context, width - halfBorderWidth, radius + halfBorderWidth);//准备开始移动坐标CGContextAddArcToPoint(context, width - halfBorderWidth, height - halfBorderWidth, width - radius - halfBorderWidth, height - halfBorderWidth, radius);CGContextAddArcToPoint(context, halfBorderWidth, height - halfBorderWidth, halfBorderWidth, height - radius - halfBorderWidth, radius);//左下角角度CGContextAddArcToPoint(context, halfBorderWidth, halfBorderWidth, width - halfBorderWidth, halfBorderWidth, radius);//左上角CGContextAddArcToPoint(context, width - halfBorderWidth, halfBorderWidth, width - halfBorderWidth, radius + halfBorderWidth, radius);CGContextDrawPath(UIGraphicsGetCurrentContext(),kCGPathFillStroke);UIImage*image =UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();returnimage;}给一个图片做出圆角:@implementationUIImageView (CornerRounder)- (void)dr_addCornerRadius:(CGFloat)radius {self.image= [self.imagedr_imageAddCornerWithRadius:radiusandSize:self.bounds.size];}@end@implementationUIImage (ImageCornerRounder)- (UIImage*)dr_imageAddCornerWithRadius:(CGFloat)radius andSize:(CGSize)size{CGRectrect =CGRectMake(0,0, size.width, size.height);UIGraphicsBeginImageContextWithOptions(size,NO, [UIScreenmainScreen].scale);CGContextRefctx =UIGraphicsGetCurrentContext();UIBezierPath* path = [UIBezierPathbezierPathWithRoundedRect:rectbyRoundingCorners:UIRectCornerAllCornerscornerRadii:CGSizeMake(radius, radius)];CGContextAddPath(ctx,path.CGPath);CGContextClip(ctx);[selfdrawInRect:rect];CGContextDrawPath(ctx,kCGPathFillStroke);UIImage* newImage =UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();returnnewImage;}@end

这样就可以就可以有效避免大量的离屏渲染拖慢fps。

当然,在圆角较少的情况下大可不必这么折腾,设置圆角的方法也有很多种,除了最常用的layer两个属性以外,还有:

1.UIBezierPath:

CGRectbounds =self.bounds;[[UIBezierPathbezierPathWithRoundedRect:rect cornerRadius:8.0] addClip];[self.imagedrawInRect:bounds];}

2.maskLayer(CAShapeLayer)

这里要感谢叶孤城大神的文章解疑,本文圆角设置方法介绍不算全面,还有待补充,只是最近项目上遇到了百年一遇的离屏渲染导致了严重卡屏问题所以就小研究了一下,并做下笔记。

0 0
原创粉丝点击