iOS 控件设置圆角,避免离屏渲染。
离屏渲染:
指GPU(图形处理器)在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作。这会给我们带来额外的性能损耗,如果这样的操作达到一定数量,会触发缓冲区的频繁合并和上下文的的频繁切换,会出现卡顿、掉帧现象。造成离屏渲染的原因有很多,如:shouldRasterize(光栅化)、mask(遮罩层)、shadows(阴影)、EdgeAnntialiasing(抗锯齿)、cornerRadius(圆角)等等。
下面说一下什么情况下设置圆角会造成离屏渲染://设置cornerRadius>0且masksToBounds为YES view.layer.cornerRadius = 10.f;view.layer.masksToBounds = YES;
//设置cornerRadius>0且masksToBounds为YES view.layer.cornerRadius = 10.f;view.clipToBounds = YES;
//像下面设置view.layer.mask UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:imgView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(10.f, 10.f)];CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];maskLayer.frame = view.bounds;maskLayer.path = path.CGPath;view.layer.mask = maskLayer;上面第一种应该是最常用的,是在设置了圆角及masksToBounds为YES时,才会触发离屏渲染,而masksToBounds默认是NO,也就是说只要不设置这个属性就能避免很多情况了,下面说一下如何切一个不触发离屏渲染的圆角:UIView、UITextField、UITextView等大部分控件都可以像下面这样设置:view.layer.cornerRadius = 10.f;view.layer.masksToBounds = NO;有一些特殊情况,UILabel设置时,不要设置label.backgroundColor,应设置:label.layer.cornerRadius = 10.f;label.layer.backgroundColor = [UIColor whiteColor].CGColor;有图片的UIButton、UIIMageView,用drawInRect绘制UIImage圆角:UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius];UIGraphicsBeginImageContextWithOptions(rect.size, false, [UIScreen mainScreen].scale);CGContextAddPath(UIGraphicsGetCurrentContext(), bezierPath.CGPath);CGContextClip(UIGraphicsGetCurrentContext());[self drawInRect:rect];
CGContextDrawPath(UIGraphicsGetCurrentContext(), kCGPathFillStroke);UIImage *image = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();查看离屏渲染,模拟器可以选中“Debug - Color Off-screen Rendered”开启调试,真机可以用Instruments检测,“Instruments - Core Animation - Debug Options - Color Offscreen-Rendered Yellow”开启调试,开启后,有离屏渲染的图层会变成高亮的黄色。
写了个离屏渲染的样例:
下面贴上代码:
控制器ViewController:#import "ViewController.h"#import "UIImage+HWCorner.h"
#define KMainW [UIScreen mainScreen].bounds.size.width
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
//创建控件[self creatControl];}
- (void)creatControl{CGFloat margin = 20.f;CGFloat controlW = (KMainW - margin * 3) * 0.5;
NSArray *titleArray = @[@"离屏渲染", @"非离屏渲染"];for (int i = 0; i < titleArray.count; i++) {CGFloat controlX = margin + (controlW + margin) * i;//UILabel UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(controlX, 30, controlW, 40)];label.layer.backgroundColor = [[UIColor grayColor] CGColor];label.text = titleArray[i];label.textAlignment = NSTextAlignmentCenter;label.layer.cornerRadius = label.bounds.size.height * 0.5;label.layer.masksToBounds = i == 0 ? YES : NO;[self.view addSubview:label];
//UIView UIView *view = [[UIView alloc] initWithFrame:CGRectMake(controlX, CGRectGetMaxY(label.frame) + margin, controlW, 40)];view.backgroundColor = [UIColor grayColor];view.layer.cornerRadius = view.bounds.size.height * 0.5;view.layer.masksToBounds = i == 0 ? YES : NO;[self.view addSubview:view];
//UIView若未添加子控件,设置view.layer.masksToBounds = YES;也不会造成离屏渲染UIView *subView = [[UIView alloc] initWithFrame:CGRectMake(30, 0, controlW - 60, 40)];subView.backgroundColor = [UIColor redColor];[view addSubview:subView];
//UITextView UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(controlX, CGRectGetMaxY(view.frame) + margin, controlW, 40)];textView.userInteractionEnabled = NO;textView.backgroundColor = [UIColor grayColor];if (i == 0) {/*这里换了一种实现方法,用UIBezierPath赋值layer.mask,两种方式都会造成离屏渲染textView.layer.cornerRadius = textView.bounds.size.height * 0.5;textView.layer.masksToBounds = YES;*/ UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:textView.bounds byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(textView.bounds.size.height * 0.5, textView.bounds.size.height * 0.5)];CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];maskLayer.frame = textView.bounds;maskLayer.path = path.CGPath;textView.layer.mask = maskLayer;}else {textView.layer.cornerRadius = textView.bounds.size.height * 0.5;textView.layer.masksToBounds = NO;}[self.view addSubview:textView];
//UIButton UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake(controlX, CGRectGetMaxY(textView.frame) + margin, controlW, controlW)];if (i == 0) {[button setImage:[UIImage imageNamed:@"hero_1"] forState:UIControlStateNormal];button.layer.cornerRadius = button.bounds.size.width * 0.5;button.layer.masksToBounds = YES;}else {[button setImage:[[UIImage imageNamed:@"hero_1"] drawCornerInRect:button.bounds cornerRadius:button.bounds.size.width * 0.5] forState:UIControlStateNormal];}[self.view addSubview:button];
//UIImageView设置圆角UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(controlX, CGRectGetMaxY(button.frame) + margin, controlW, controlW)];if (i == 0) {[imageView setImage:[UIImage imageNamed:@"hero_1"]];imageView.layer.cornerRadius = imageView.bounds.size.width * 0.5;imageView.layer.masksToBounds = YES;}else {[imageView setImage:[[UIImage imageNamed:@"hero_1"] drawCornerInRect:imageView.bounds cornerRadius:imageView.bounds.size.width * 0.5]];}[self.view addSubview:imageView];
//UIImageView若未添加子控件,设置imageView.layer.masksToBounds = YES;也不会造成离屏渲染UIView *subImageView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, controlW, 40)];subImageView.backgroundColor = [UIColor redColor];subImageView.layer.cornerRadius = imageView.bounds.size.width * 0.5;[imageView addSubview:subImageView];
//UIImageView设置阴影CGFloat imgW = 70.f;CGFloat imgPadding = (KMainW - imgW * 4 - margin * 2) / 3;UIImageView *shadowImgView = [[UIImageView alloc] initWithFrame:CGRectMake(margin + (imgW + imgPadding) * 2 * i, CGRectGetMaxY(imageView.frame) + margin, imgW, imgW)];[shadowImgView setImage:[UIImage imageNamed:@"hero_1"]];shadowImgView.layer.shadowColor = [UIColor redColor].CGColor;shadowImgView.layer.shadowOpacity = 0.8f;shadowImgView.layer.shadowOffset = CGSizeMake(5, 5);shadowImgView.layer.shadowRadius = 5.f;if (i == 1) {UIBezierPath *path = [UIBezierPath bezierPathWithRect:shadowImgView.bounds];shadowImgView.layer.shadowPath = path.CGPath;}[self.view addSubview:shadowImgView];
//UIImageView设置阴影+圆角UIImageView *shadowCorImgView = [[UIImageView alloc] initWithFrame:CGRectMake(margin + imgW + imgPadding + (imgW + imgPadding) * 2 * i, CGRectGetMinY(shadowImgView.frame), imgW, imgW)];[shadowCorImgView setImage:[[UIImage imageNamed:@"hero_1"] drawCornerInRect:imageView.bounds cornerRadius:imageView.bounds.size.width * 0.5]];shadowCorImgView.layer.shadowColor = [UIColor redColor].CGColor;shadowCorImgView.layer.shadowOpacity = 0.8f;shadowCorImgView.layer.shadowOffset = CGSizeMake(0, 0);shadowCorImgView.layer.shadowRadius = 5.f;if (i == 1) {UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:shadowCorImgView.bounds cornerRadius:shadowCorImgView.bounds.size.height * 0.5];shadowCorImgView.layer.shadowPath = path.CGPath;}[self.view addSubview:shadowCorImgView];}}
@endUIImage分类,UIImage+HWCorner:#import
@interface UIImage (HWCorner)
//绘制图片圆角- (UIImage *)drawCornerInRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius;
@end
/*** ---------------分割线--------------- ***/
#import "UIImage+HWCorner.h"
@implementation UIImage (HWCorner)
//绘制图片圆角- (UIImage *)drawCornerInRect:(CGRect)rect cornerRadius:(CGFloat)cornerRadius{UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:cornerRadius];UIGraphicsBeginImageContextWithOptions(rect.size, false, [UIScreen mainScreen].scale);CGContextAddPath(UIGraphicsGetCurrentContext(), bezierPath.CGPath);CGContextClip(UIGraphicsGetCurrentContext());[self drawInRect:rect];
CGContextDrawPath(UIGraphicsGetCurrentContext(), kCGPathFillStroke);UIImage *image = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();
return image;}
@end最后说一下,只有在大量的离屏渲染情况时才会出现卡顿、掉帧等现象,没必要过分追求容不下一丝黄色。性能上Instruments工具还可以检测很多。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。
您可能感兴趣的文章:IOS 性能优化中离屏渲染iOS中图片的解压缩到渲染过程详解iOS图片压缩、滤镜、剪切及渲染等详解iOS离屏渲染过程示例解析
