离屏渲染:指GPU(图形处理器)在当前屏幕缓冲区外新开辟一个渲染缓冲区进行工作。这会给我们带来额外的性能损耗,如果这样的操作达到一定数量,会触发缓冲区的频繁合并和上下文的的频繁切换,会出现卡顿、掉帧现象。造成离屏渲染的原因有很多,如:shouldRasterize(光栅化)、mask(遮罩层)、shadows(阴影)、EdgeAnntialiasing(抗锯齿)、cornerRadius(圆角)等等。
下面说一下什么情况下设置圆角会造成离屏渲染:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
//设置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等大部分控件都可以像下面这样设置:
1
2
|
view.layer.cornerRadius = 10.f; view.layer.masksToBounds = NO; |
有一些特殊情况,UILabel设置时,不要设置label.backgroundColor,应设置:
1
2
|
label.layer.cornerRadius = 10.f; label.layer.backgroundColor = [UIColor whiteColor].CGColor; |
有图片的UIButton、UIIMageView,用drawInRect绘制UIImage圆角:
1
2
3
4
5
6
7
8
9
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
#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]; } } @end |
UIImage分类,UIImage+HWCorner:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
#import <UIKit/UIKit.h> @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工具还可以检测很多。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://blog.csdn.net/hero_wqb/article/details/79085046