很早就想写一篇讲述圆角优化的博客了。。结果发现这篇文章iOS 高效添加圆角效果实战讲解写得很好:摘录文章的总结部分,当拓展阅读了:
总结
如果能够只用 cornerRadius 解决问题,就不用优化。
如果必须设置 masksToBounds,可以参考圆角视图的数量,如果数量较少(一页只有几个)也可以考虑不用优化。
UIImageView 的圆角通过直接截取图片实现,其它视图的圆角可以通过 Core Graphics 画出圆角矩形实现。
过早的优化是恶魔,这句话确实很有道理
Premature optimization is the root of all evil --Donald Knuth
当时我做圆角优化的场景,是一个collectionView很多头像的展示。如果单纯的用layer设置cornerRadius和masksToBounds,会产生离屏渲染,小心别让圆角成了你列表的帧数杀手,这篇文章已经写的很清楚了,就不赘述了。
self.xxView.layer.cornerRadius = 5.0f;
self.xxView.layer.masksToBounds = YES;
Advanced Graphics and Animations for iOS Apps(session 419) 学习与延伸这篇文章也介绍了离屏渲染的一些知识,有需要的同学可以去拓展阅读一下。
正文
其实写到这里我已经很方了。。因为感觉好多人都写过了。。
这边再留两篇文章:
最早接触到圆角优化是里脊串写的一次对MKMapView的性能优化
近来还有叶孤城___的reviewcode.cn里提出的解决方案。
你们可以先看我的文章,然后再去拓展阅读,反正上面那么多链接你们也要看一会[认真脸]。
既然原理上面的文章都讲过很多了,我真不好意思再讲一遍了,我就来说说我怎么做的吧。
首先给imageView一个catagory,核心方法如下:
- (void)lhy_loadImageUrlStr:(NSString *)urlStr placeHolderImageName:(NSString *)placeHolderStr radius:(CGFloat)radius;
当然也有些简便的方法方便不同场景调用:
- (void)lhy_loadImageUrlStr:(NSString *)urlStr;
- (void)lhy_loadImageUrlStr:(NSString *)urlStr radius:(CGFloat)radius;
核心方法是这样写的:
- (void)lhy_loadImageUrlStr:(NSString *)urlStr placeHolderImageName:(NSString *)placeHolderStr radius:(CGFloat)radius {
//something
//这里有针对不同需求的处理,我就没贴出来了
//...
NSURL *url;
if (placeHolderStr == nil) {
placeHolderStr = @"你通用的占位图地址";
}
//这里传CGFLOAT_MIN,就是默认以图片宽度的一半为圆角
if (radius == CGFLOAT_MIN) {
radius = self.frame.size.width/2.0;
}
url = [NSURL URLWithString:urlStr];
if (radius != 0.0) {
//头像需要手动缓存处理成圆角的图片
NSString *cacheurlStr = [urlStr stringByAppendingString:@"radiusCache"];
UIImage *cacheImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:cacheurlStr];
if (cacheImage) {
self.image = cacheImage;
}
else {
[self sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:placeHolderStr] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
if (!error) {
UIImage *radiusImage = [UIImage createRoundedRectImage:image size:self.frame.size radius:radius];
self.image = radiusImage;
[[SDImageCache sharedImageCache] storeImage:radiusImage forKey:cacheurlStr];
//清除原有非圆角图片缓存
[[SDImageCache sharedImageCache] removeImageForKey:urlStr];
}
}];
}
}
else {
[self sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:placeHolderStr] completed:nil];
}
}
圆角的绘制方法是一个imageView的catagory
static void addRoundedRectToPath(CGContextRef context, CGRect rect, float ovalWidth,
float ovalHeight)
{
float fw, fh;
if (ovalWidth == 0 || ovalHeight == 0)
{
CGContextAddRect(context, rect);
return;
}
CGContextSaveGState(context);
CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGContextScaleCTM(context, ovalWidth, ovalHeight);
fw = CGRectGetWidth(rect) / ovalWidth;
fh = CGRectGetHeight(rect) / ovalHeight;
//根据圆角路径绘制
CGContextMoveToPoint(context, fw, fh/2);
CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);
CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1);
CGContextClosePath(context);
CGContextRestoreGState(context);
}
+ (id)createRoundedRectImage:(UIImage*)image size:(CGSize)size radius:(NSInteger)r
{
int w = size.width;
int h = size.height;
UIImage *img = image;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, (CGBitmapInfo)kCGImageAlphaPremultipliedFirst);
//CGContextRef context = UIGraphicsGetCurrentContext();
CGRect rect = CGRectMake(0, 0, w, h);
CGContextBeginPath(context);
addRoundedRectToPath(context, rect, r, r);
CGContextClosePath(context);
CGContextClip(context);
CGContextDrawImage(context, CGRectMake(0, 0, w, h), img.CGImage);
CGImageRef imageMasked = CGBitmapContextCreateImage(context);
img = [UIImage imageWithCGImage:imageMasked];
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
CGImageRelease(imageMasked);
return img;
}
这样只要两个catagory,这个头像圆角结合SDWebImage缓存的应用场景,就可以即插即用啦。
简书已经弃用,欢迎移步我的小专栏:
https://xiaozhuanlan.com/dahuihuiiOS
网友评论
// 保存图片到磁盘
- (void)storeImage:(nullable UIImage *)image
forKey:(nullable NSString *)key
completion:(nullable SDWebImageNoParamsBlock)completionBlock;
// 清除原有非圆角图片缓存
- (void)removeImageForKey:(nullable NSString *)key withCompletion:(nullable SDWebImageNoParamsBlock)completion;
但这句话 的if条件
```objc
if (radius != 0.0)
```
是不是改成
```objc
if (radius > 0.0001)
```
会更好些
1.图片没有按照控件的比例来进行圆角绘制
2.SDWebImage获取下来的图片会根据设备适配,比实际尺寸小2/1.5倍
这样导致图片需要的部分不对,也被拉伸了
在UIImageView分类SDWebImage下载图片成功回调那儿,改用下面段方法绘制成圆角图片就可以了。
UIImage *radiusImage = [image drawRectWithRoundedCorner:radius size:self.bounds.size]; // 得到清晰圆角图片
UIImage分类:
- (UIImage *)drawRectWithRoundedCorner:(CGFloat)radius size:(CGSize)sizeToFit
{
CGRect rect = CGRectMake(0, 0, sizeToFit.width, sizeToFit.height);
UIGraphicsBeginImageContextWithOptions(rect.size, false, [UIScreen mainScreen].scale);
CGContextAddPath(UIGraphicsGetCurrentContext(), [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)].CGPath);
CGContextClip(UIGraphicsGetCurrentContext());
[self drawInRect:rect];
CGContextDrawPath(UIGraphicsGetCurrentContext(), kCGPathFillStroke);
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return outputImage;
}