基本方法
使用layer的cornerRadius属性和masksToBounds。
self.imageView.layer.cornerRadius = 8.f;
self.imageView.layer.masksToBounds = YES;
这样设置圆角是最基本的一种方式,但当屏幕中有多个圆角图片存在会发生明显的界面卡顿现象。
原因是cornerRadius和masksToBounds同时使用会发生离屏渲染(离屏渲染参考每日一问03)
值得一提的是iOS9以后,cornerRadius = YES
在UIImageView上不会发生离屏渲染,但在button等其他控件上使用还是会发生。
这种方法使用场景就是同界面中没有过多的圆角图片。
基本方案的优化
setCornerRadius设置圆角之后,shouldRasterize=YES
光栅化
原理是开启了光栅化以后,可以使离屏渲染的结果缓存到内存中存为位图, 使用的时候直接使用缓存,节省了一直离屏渲染损耗的性能。
问题:
1.被光栅化的图片如果超过100ms没有被使用,则会被移除
2.系统限制了缓存的大小为2.5X Screen Size.如果过度使用,超出缓存之后,同样会造成大量的offscreen渲染
所以如果不是同一张图片多次使用,这种方案也不可取。
使用 mask layer
UIBezierPath* maskPath = [UIBezierPath bezierPathWithRoundedRect:imageView.bounds byRoundingCorners:UIRectCornerAllCorners cornerRadii:imageView.bounds.size];
CAShapeLayer* maskLayer = [CAShapeLayer layer];
maskLayer.frame = imageView.bounds;
maskLayer.path = maskPath.CGPath;
imageView.layer.mask = maskLayer;
这种方法本质上是用遮罩层 mask 来实现,因此同样无可避免的会导致离屏渲染。
使用混合图层
在要添加圆角的视图上再叠加一个部分透明的视图,只对圆角部分进行遮挡。多一个图层会增加合成的工作量,但这点工作量与离屏渲染相比微不足道,性能上无论各方面都和无效果持平。
这种方式在性能上非常的好,但是缺点就是角度不同需要设计师提供多张不同的图片,非常不灵活。
直接裁剪image
实现一,这个方案的思路是将离屏渲染的消耗从GPU转交给CPU,让CPU提前处理一下bitmap数据。这算是CPU的离屏渲染。
CGRect rect = CGRectMake(0, 0, size.width, size.height);
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
CGContextRef ctx = UIGraphicsGetCurrentContext();
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)];
CGContextAddPath(ctx,path.CGPath);
CGContextClip(ctx);
[self drawInRect:rect];
CGContextDrawPath(ctx, kCGPathFillStroke);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
实现二,这个方案采用SDWebImage处理图片时Core Graphics绘制圆角。
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); // Start at lower right corner
CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1); // Top right corner
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1); // Top left corner
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1); // Lower left corner
CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1); // Back to lower right
CGContextClosePath(context);
CGContextRestoreGState(context);
}
- (UIImage *)createImageWith:(CGSize)imageSize Radius:(CGFloat)radius{
int w = imageSize.width;
int h = imageSize.height;
UIImage *img = self;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
CGRect rect = CGRectMake(0, 0, w, h);
CGContextBeginPath(context);
addRoundedRectToPath(context, rect, radius, radius);
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;
}
同样没有离屏渲染但这个方案在性能上比方案一更好,主要体现在CPU和内存消耗更低。从实现上来分析,方案二的优点主要是采用了强制解压生成位图。优化了CPU进行解压那一块的消耗。(参考每日一问04)
总结:圆角优化这个话题虽然已经过去很久,网上也给出了很多成熟的解决方案,但导致需要优化的原理和优化方案的原理我们还是应该了解的。
相关文章
iOS 高效添加圆角效果实战讲解
iOS 高效添加圆角效果实战讲解-
圆角卡顿刨根问底
iOS图片高性能设置圆角
iOS UIView 圆角和加边框方式总结