当我们需要给一个 View 设置圆角时,通常这样写
_label.layer.cornerRadius = 10;
_label.layer.masksToBounds = YES;
当 cornerRadius 和 maskToBounds 独立作用时不会有什么性能问题,但是如果它们结合在一起,就会触发送屏渲染,Instrument的 Core Animation 有一个名为 Color Offscreen-Rendered Yellow 的选项。它将已经绘制到屏幕外缓冲区的区域标记为黄色,下图中的黄色部分就是离屏渲染的地方。
离屏渲染将 layer tree 中的一部分绘制到一个新的缓存中(这个缓存不是屏幕,而是在另一个地方),然后再将这个缓存绘制到屏幕上。一般来说,你需要避免离屏渲染,因为这个开销很大。在屏幕上直接合成层要比先创建一个离屏缓存然后在缓存上绘制,最后再将缓存绘制到屏幕上快得多。这里有两个上下文环境的切换(切换到屏幕外缓存环境,和屏幕环境)。
如果你的 view 不需要让子视图超出部分不显示,并且不需要给 view 的 image 绘制圆角,
可以查看 cornerRadius 属性的注释:
By default, the corner radius does not apply to the image in the layer’s contents property; it applies only to the background color and border of the layer. However, setting the masksToBounds property to true causes the content to be clipped to the rounded corners.
这个属性会影响 layer 的背景颜色和 border,因此以下代码可以避免离屏渲染。
view.layer.cornerRadius = radius;
view.layer.backgroundColor = backgroundColor.CGColor;
但如果需要给 view 的 image 绘制圆角,如 UIImageView.image 和 UIButton 的背景图片。
则可以使用 GraphicsContext 绘制一个带圆角的 image 给 view 来避免离屏渲染。
我将这个过程封装了一下
platform :ios, '7.0'
pod 'JMRoundedCorner'
#import "UIView+RoundedCorner.h"
- (void)jm_setCornerRadius:(CGFloat)radius withBorderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth;
- (void)jm_setCornerRadius:(CGFloat)radius withBackgroundColor:(UIColor *)backgroundColor;
- (void)jm_setCornerRadius:(CGFloat)radius withImage:(UIImage *)image;
- (void)jm_setCornerRadius:(CGFloat)radius withImage:(UIImage *)image contentMode:(UIViewContentMode)contentMode;
- (void)jm_setCornerRadius:(CGFloat)radius withBorderColor:(UIColor *)borderColor borderWidth:(CGFloat)borderWidth backgroundColor:(UIColor *)backgroundColor backgroundImage:(UIImage *)backgroundImage contentMode:(UIViewContentMode)contentMode;
_avatarView = [[UIImageView alloc] initWithFrame:CGRectMake(10, 7, 40, 40)];
[_avatarView jm_setCornerRadius:20 withImage:[UIImage imageNamed:@"avatar.jpg"]];
[self.contentView addSubview:_avatarView];
//添加占位图
_avatarView.image = [placeholderImage jm_imageWithRoundedCornersAndSize:CGSizeMake(60, 60) andCornerRadius:30];
//下载完之后设置圆角 image
[[SDWebImageManager sharedManager] downloadImageWithURL:_model.avatarURL options:0 progress:nil completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, BOOL finished, NSURL *imageURL) {
if (image) {
[_avatarView jm_setCornerRadius:30 withImage:image];
}
}];
这样,绘制出了圆角,也可以避免在大量 view 离屏渲染时拖慢 FPS
下载 Demo,尝试使用 JMRoundedCorner 带来的流畅提升。
[_label jm_setJMRadius:JMRadiusMake(0, 10, 0, 10) withBorderColor:[UIColor redColor] borderWidth:0.5];
优点:没有离屏渲染,调整了图片的像素大小以避免不必要的缩放
缺点:会造成图层混合,并且仅仅绘制了一个带圆角的图片,因此不能使子视图在圆角部分不显示。 注意:内存会持续增加,是正常现象,点击 home 键内存会回到正常水平,并非内存泄漏,只是绘制的缓存,在内存不足时会自动释放。
2016/4/25 1.2.0版本 : 使用 NSOperationQueue 代替 dispatch_queue,当重复设置圆角时,会自动取消上一次操作,感谢 kudocc 的 pull request。
2016/4/18 1.1.2版本 : 修改一点小BUG
2016/3/14 1.1.1版本 : 解决设置 contentMode 效果的一些BUG。
2016/3/12 1.1.0版本 : 接口带有 jm_ 前缀,JMRadius 添加圆角优先级,经过大量测试,解决了一些细节上的小BUG。
2016/3/6 1.0.5版本 : 优化
2016/3/4 1.0.4版本 : 修复有背景图片时不绘制背景颜色的BUG
2016/3/3 1.0.3版本 : 修复 label 中如果没有汉字,文字不显示的BUG,以及做了使 view 落在像素点上的优化。
2016/3/1 1.0.2版本 :修复 backgroundColor 参数无效的BUG
2016/3/1 1.0.1版本 :优化
2016/2/28 1.0.0版本 :发布正式版本
2016/2/26 0.0.4版本 :去掉了 size 参数,并支持 JMRadius 设置4个角为不同的曲率角度
2016/2/25 0.0.3版本 :去掉了 UIImageView 这个中间控件
2016/2/24 0.0.2版本 :支持设置背景图片的绘制模式(contentMode)
2016/2/23 0.0.1版本 :绘制一个圆角图片