点击下载 Demo
一、CornerRadius 设置圆角
如果仅仅是view需要设置圆角,是不要设置maskToBounds的,并且someView.layer.cornerRadius并不会触发离屏渲染(Off-Screen Rendering)。当界面中有其他的包含子控件的view,如label,imageView等,需要设置圆角,但是数量并不多。此时可以安心的使用如下的方法,这里虽然会触发离屏渲染,但是由于数量比较少,所以对全局的影响一般不会很大。
self.iconView.layer.cornerRadius = self.iconView.frame.size.height / 2;
self.iconView.layer.masksToBounds = YES;
离屏渲染(Off-Screen Rendering):指的是 GPU(图形处理器)在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。离屏渲染耗时是发生在离屏这个动作上面,而不是渲染。为什么离屏这么耗时?原因主要有创建缓冲区和上下文切换。创建新的缓冲区代价都不算大,付出最大代价的是上下文切换。
上下文切换:不管是在 GPU 渲染过程中,还是一直所熟悉的进程切换,上下文切换在哪里都是一个相当耗时的操作。首先我要保存当前屏幕渲染环境,然后切换到一个新的绘制环境,申请绘制资源,初始化环境,然后开始一个绘制,绘制完毕后销毁这个绘制环境,如需要切换到On-Screen Rendering或者再开始一个新的离屏渲染重复之前的操作。
注意:ios9.0之后对UIImageView的圆角设置做了优化,UIImageView这样设置圆角不会触发离屏渲染,ios9.0之前还是会触发离屏渲染。而UIButton还是都会触发离屏渲染。
二、Core Graphics 生成圆角图片
使用 Graphics 绘制新的图片,需要大量计算和增加部分内存
- (UIImage *)radiusImageWithImage:(UIImage *)image withRadius:(CGFloat)radius {
UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale);
CGRect roundRect = CGRectMake(0, 0, image.size.width, image.size.height);
if (!radius) {
radius = image.size.width / 2.0;
}
[[UIBezierPath bezierPathWithRoundedRect:roundRect
cornerRadius:radius] addClip];
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
UIImage *radiusImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return radiusImg;
}
三、UIBezierPath CAShapeLayer
使用CAShapeLayer来切割视图控件。CAShapeLayer是基于贝塞尔曲线而存在的, 如果没有贝塞尔曲线提供路径来画出图形, CAShapeLayer就没有存在的意义, CAShapeLayer可以使得不用在drawRect:方法中实现画图。另外, CAShapeLayer是属于CoreAnimation框架的, 是基于GPU的来进行渲染的, 不比使用CoreGraphic框架, 是基于CPU来渲染的, 所以CAShapeLayer效率相对比较高一些
- (void)circleViewWithView:(UIView *)view radius:(CGFloat)radius {
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:view.bounds
cornerRadius:radius];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = view.bounds;
maskLayer.path = maskPath.CGPath;
view.layer.mask = maskLayer;
}
四、覆盖镂空图片
此方法就是在要添加圆角的视图上再叠加一个部分透明的视图,只对圆角部分进行遮挡。图层混合的透明度处理方式与mask正好相反。此方法虽然是最优解,没有离屏渲染,没有额外的CPU计算,但是应用范围有限。
- (void)method4 {
UIImageView *iconCover= [[UIImageView alloc] initWithFrame:self.iconView.frame];
iconCover.image = [UIImage imageNamed:@"imageCornerCover"];
[self addSubview:iconCover];
UIImageView *imgCover = [[UIImageView alloc] initWithFrame:self.imgView.frame];
imgCover.image = [UIImage imageNamed:@"imageCornerCover"];
[self addSubview:imgCover];
}
五、SDWebImage + Graphics
使用 Graphics 切图,和 SDWebImage 相结合,把 Graphics 切图的耗时操作放在子线程处理,处理完使用 SDWebImage 存储,然后再给控件赋值。
- (void)circleImageWithUrl:(NSURL *)imgUrl
placeholder:(UIImage *)placeholder
rate:(CGFloat)rate {
NSString *imgkey = [self keyWithImageUrl:imgUrl];
UIImage *circleImg = [[SDImageCache sharedImageCache] imageFromCacheForKey:imgkey];
if (circleImg) {
self.image = circleImg;
return;
}
if (self.loadingImageUrlStr) {
// 取消上一个图片绘制
[self.cancelLoadingArr addObject:self.loadingImageUrlStr];
}
__weak typeof(self)wkSlef = self;
[self sd_setImageWithURL:imgUrl
placeholderImage:placeholder
options:SDWebImageRetryFailed | SDWebImageAvoidAutoSetImage
completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) {
[wkSlef backgroundDrawImageForImage:image
cacheWithUrl:imageURL
rate:rate];
}];
}
- (void)backgroundDrawImageForImage:(UIImage *)image
cacheWithUrl:(NSURL *)imgUrl
rate:(CGFloat)rate {
[self setLoadingImageUrlStr:imgUrl.absoluteString];
__weak typeof(self)wkSlef = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale);
CGRect roundRect = CGRectMake(0, 0, image.size.width, image.size.height);
CGFloat radius = image.size.width * rate;
[[UIBezierPath bezierPathWithRoundedRect:roundRect
cornerRadius:radius] addClip];
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
UIImage *radiusImg = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
[[SDImageCache sharedImageCache] storeImage:radiusImg
forKey:[wkSlef keyWithImageUrl:imgUrl]
completion:nil];
if ([wkSlef.cancelLoadingArr containsObject:imgUrl.absoluteString]) {
[wkSlef.cancelLoadingArr removeObject:imgUrl.absoluteString];
} else {
wkSlef.image = radiusImg;
}
});
});
}
下载Demo:https://github.com/ZhangJingHao/ZJHCornerImage
参考链接:http://www.jianshu.com/p/8c89460fc676
网友评论