基本方法
使用layer的cornerRadius屬性和masksToBounds刃泡。
self.imageView.layer.cornerRadius = 8.f;
self.imageView.layer.masksToBounds = YES;
這樣設(shè)置圓角是最基本的一種方式,但當(dāng)屏幕中有多個圓角圖片存在會發(fā)生明顯的界面卡頓現(xiàn)象闺金。
原因是cornerRadius和masksToBounds同時(shí)使用會發(fā)生離屏渲染(離屏渲染參考每日一問03)
值得一提的是iOS9以后逾滥,cornerRadius = YES
在UIImageView上不會發(fā)生離屏渲染,但在button等其他控件上使用還是會發(fā)生败匹。
這種方法使用場景就是同界面中沒有過多的圓角圖片寨昙。
基本方案的優(yōu)化
setCornerRadius設(shè)置圓角之后,shouldRasterize=YES
光柵化
原理是開啟了光柵化以后掀亩,可以使離屏渲染的結(jié)果緩存到內(nèi)存中存為位圖舔哪, 使用的時(shí)候直接使用緩存,節(jié)省了一直離屏渲染損耗的性能槽棍。
問題:
1.被光柵化的圖片如果超過100ms沒有被使用,則會被移除
2.系統(tǒng)限制了緩存的大小為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;
這種方法本質(zhì)上是用遮罩層 mask 來實(shí)現(xiàn)炼七,因此同樣無可避免的會導(dǎo)致離屏渲染缆巧。
使用混合圖層
在要添加圓角的視圖上再疊加一個部分透明的視圖,只對圓角部分進(jìn)行遮擋豌拙。多一個圖層會增加合成的工作量盅蝗,但這點(diǎn)工作量與離屏渲染相比微不足道,性能上無論各方面都和無效果持平姆蘸。
這種方式在性能上非常的好墩莫,但是缺點(diǎn)就是角度不同需要設(shè)計(jì)師提供多張不同的圖片,非常不靈活逞敷。
直接裁剪image
實(shí)現(xiàn)一狂秦,這個方案的思路是將離屏渲染的消耗從GPU轉(zhuǎn)交給CPU,讓CPU提前處理一下bitmap數(shù)據(jù)推捐。這算是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;
實(shí)現(xiàn)二,這個方案采用SDWebImage處理圖片時(shí)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;
}
同樣沒有離屏渲染但這個方案在性能上比方案一更好堪簿,主要體現(xiàn)在CPU和內(nèi)存消耗更低痊乾。從實(shí)現(xiàn)上來分析,方案二的優(yōu)點(diǎn)主要是采用了強(qiáng)制解壓生成位圖椭更。優(yōu)化了CPU進(jìn)行解壓那一塊的消耗哪审。(參考每日一問04)
總結(jié):圓角優(yōu)化這個話題雖然已經(jīng)過去很久,網(wǎng)上也給出了很多成熟的解決方案虑瀑,但導(dǎo)致需要優(yōu)化的原理和優(yōu)化方案的原理我們還是應(yīng)該了解的湿滓。
相關(guān)文章
iOS 高效添加圓角效果實(shí)戰(zhàn)講解
iOS 高效添加圓角效果實(shí)戰(zhàn)講解-簡書
圓角卡頓刨根問底
iOS圖片高性能設(shè)置圓角
iOS UIView 圓角和加邊框方式總結(jié)