- iOS中实现UIView及其子类的圆角效果有2中方法
- 检测开启离屏渲染
- 实现圆角两部曲
- 设置圆角就一定会触发离屏渲染吗
** 不会触发离屏渲染的情况1
**不会触发离屏渲染的情况2
** 会触发离屏渲染的情况1
** 会触发离屏渲染的情况2 - 绘制图层的流程
- 绘制圆角时出现离屏渲染的解决方案
iOS中实现UIView及其子类的圆角效果有2中方法
- 当前屏幕渲染,GPU直接在当前显示的屏幕的缓冲区进行图形渲染,不需要提前另开缓冲区也就不需要缓冲区的切换,因此性能更高
- 离屏渲染,简单的来说就是提前另开一个缓冲区进行图形渲染,由于显示需要和当前屏幕缓冲区进行切换,所以很耗费性能,通常圆角,遮罩,不透明度,阴影,渐变,光栅化和抗锯齿等这些都会触发离屏渲染
检测开启离屏渲染
如图,如果触发了离屏渲染,会有浅黄色背景出现
实现圆角两部曲
im.layer.cornerRadius = 5;//设置圆角
im.layer.masksToBounds = YES;//裁减
没错,就是这么简单,那么你知道为什么要设置2个属性吗,既然是搭配使用,又是万年重复的代码,一个属性不好吗,那么请看
是的,旁边的汉字说明一件非常清楚,因为他俩各司其职。
那么问题又来了,少设置一个行不行,首先第一步是不能省的,第二步呢,请看下方 不会触发离屏渲染的情况1
设置圆角就一定会触发离屏渲染吗
绝大部分未深入研究的人肯定觉得设置圆角就一定会触发离屏渲染,事实并非如此,来看一张图
可以看出,触发离屏渲染需要3个条件
- 1.contents 即:图片(设置图片即意味着添加了内容contents)
- 2.背景色 或 border ,为什么说是或而不是和,因为他俩是2个图层,超过一个图册的渲染就会触发离屏渲染
- 不会触发离屏渲染的情况1
//不添加内容只设置背景色
UIImageView *im = [[UIImageView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
//设置了背景颜色,仅有一个图层
im.backgroundColor = [UIColor redColor];
//设置圆角属性
im.layer.cornerRadius = 50;//圆角
//既然视图只有一个图层,还需要裁减吗,答案是不需要,
im.layer.masksToBounds = YES;//裁减-无影响
[self.view addSubview:im];
- 不会触发离屏渲染的情况2
//设置了图片,不设置背景色和border 无触发,如下,是不会触发离屏渲染
UIImageView *im = [[UIImageView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
//设置了背景颜色和图片
UIImage *ima = [UIImage imageNamed:@"girl"];
//设置圆角属性
im.layer.cornerRadius = 50;//圆角
im.layer.masksToBounds = YES;//裁减
[im setImage:ima];
[self.view addSubview:im];
- 会触发离屏渲染的情况1
//设置了背景颜色和图片以及border属性
//注意:border和背景色存在其一即可触发离屏渲染
UIImageView *im = [[UIImageView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
//设置了背景颜色和图片
im.backgroundColor = [UIColor redColor];
UIImage *ima = [UIImage imageNamed:@"girl"];
//设置圆角属性
im.layer.cornerRadius = 50;//圆角
im.layer.masksToBounds = YES;//裁减
// 设置border宽度和颜色
im.layer.borderWidth = 2.0;
im.layer.borderColor = UIColor.blackColor.CGColor;
[im setImage:ima];
[self.view addSubview:im];
- 会触发离屏渲染的情况2
//创建UIImageView
UIImageView *im = [[UIImageView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
im.layer.cornerRadius = 50;//
im.layer.masksToBounds = YES;//将边界以外区域遮盖
[self.view addSubview:im];
//创建子view
UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100.0, 100.0)];
// 下面3个任何一个属性
// 设置背景色
// view2.backgroundColor = UIColor.blueColor;
// // 设置内容
// view2.layer.contents = (__bridge id)([UIImage imageNamed:@"girl"].CGImage);
// // 设置边框
// view2.layer.borderWidth = 2.0;
// view2.layer.borderColor = UIColor.blackColor.CGColor;
[im addSubview:view2];
绘制图层的流程
看了上面3个例子,不知道大家有何感想,那么接下来提一下绘制图层的流程,不得不提到一个关键词 :油画算法
油画算法(画家算法):先绘制场景中的离观察者较远的物体,再绘制较近的物体。
在看这个图片,绘制流程是这样的(3个图层)
1.橘黄色背景,就像一个view,只加了背景色
2.中间的图片,就是添加的内容,即contents
3.最前面的border,呃,就是border
那么在内存中他们是怎么操作的呢
1.绘制背景,存放在离屏缓冲区
2.绘制出contents,存放在离屏缓冲区
3.绘制border,存放在离屏缓冲区
4.3个图层依照画家算法显示在屏幕上,并清除离屏缓冲区
5.进行圆角操作和裁减
延伸非离屏渲染的情况
1.绘制背景,存放在帧缓冲区
2.显示在屏幕上,然后清除帧缓冲区
3.绘制出contents,存放在帧缓冲区
4.显示在屏幕上,然后清除帧缓冲区
5.绘制border,存放在离屏缓冲区
6.显示在屏幕上,然后清除帧缓冲区
7.而此时,想要修改圆角,之前的图层已经丢弃,那么不就emmmm了
扩展
帧缓存区渲染流程:APP -> 帧缓存区 ->display
离屏渲染流程:APP -> 离屏缓存区 ->display
绘制圆角时出现离屏渲染的解决方案
当前屏幕渲染实现圆角(直接在当前屏幕渲染绘制,提高性能)
为UIImage类扩展一个实例方法
//当前屏幕渲染, 扩展UIimage
-(UIImage *)imageWithCornerRadius:(CGFloat)radius ofsize:(CGSize)size
{
//边界问题
if(radius < 0){
radius = 0;
}else if (radius > MIN(size.height, size.width)){
//如果radius大于最小边,取最小边的一般
radius = MIN(size.height, size.width)/2;
}
//当前image的可见绘制区域
CGRect rect = (CGRect){0, 0, size};
//创建基于位图的上下文
UIGraphicsBeginImageContextWithOptions(size, NO, UIScreen.mainScreen.scale);//scale:范围
/*
//在当前位图的上下文添加圆角绘制路径
CGContextAddPath(UIGraphicsGetCurrentContext(), [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius].CGPath);
//当前绘制路径和原绘制路径相交得到最终裁减绘制路径
CGContextClip(UIGraphicsGetCurrentContext());
*/
//等效于上面的2句代码
[[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius]addClip];
//绘制
[self drawInRect:rect];
//取得裁减后的image
UIImage *image =UIGraphicsGetImageFromCurrentImageContext();
//关闭当前位图上下文
UIGraphicsEndImageContext();
return image;
}
需要的地方调用
UIImageView *im = [[UIImageView alloc]initWithFrame:CGRectMake(10, 10, 100, 100)];
UIImage *ima = [UIImage imageNamed:@"0"];
ima = [ima imageWithCornerRadius:50 ofsize:im.frame.size];
[im setImage:ima];
[self.view addSubview:im];