肯定不是使用cornerRadius!!! 否則這題目不是顯得很弱智。本文會先講為什么不能這么做,再講應(yīng)該如何做
為什么不能使用 cornerRadius
注意審題,題目說的是高性能。那么我們首先要知道使用 cornerRadius 為何會使得性能下降。
首先說一下 GPU 的兩種渲染方式,當(dāng)前屏幕渲染和離屏渲染。
On-Screen Rendering
意為當(dāng)前屏幕渲染,指的是GPU的渲染操作是在當(dāng)前用于顯示的屏幕緩沖區(qū)中進(jìn)行。Off-Screen Rendering
意為離屏渲染,指的是GPU在當(dāng)前屏幕緩沖區(qū)以外新開辟一個(gè)緩沖區(qū)進(jìn)行渲染操作。
再來看代碼:
imageView.layer.cornerRadius = CGFloat(5);
imageView.layer.masksToBounds = YES;
如上面代碼,直接使用 cornerRadius 對 imageView 設(shè)置圓角,其渲染方式為 GPU 離屏渲染。而相比于當(dāng)前屏幕渲染,離屏渲染的代價(jià)是很高的,主要體現(xiàn)在兩個(gè)方面:
1.創(chuàng)建新緩沖區(qū)
要想進(jìn)行離屏渲染,首先要?jiǎng)?chuàng)建一個(gè)新的緩沖區(qū)。
2.上下文切換
離屏渲染的整個(gè)過程,需要多次切換上下文環(huán)境:先是從當(dāng)前屏幕(On-Screen)切換到離屏(Off-Screen),等到離屏渲染結(jié)束以后,將離屏緩沖區(qū)的渲染結(jié)果顯示到屏幕上有需要將上下文環(huán)境從離屏切換到當(dāng)前屏幕。而上下文環(huán)境的切換是要付出很大代價(jià)的。
會觸發(fā)離屏渲染的操作還有很多,想了解可以自行谷歌或百度
請注意:并非不能使用 cornerRadius 來加圓角,只是如果當(dāng)前屏幕中這樣的圓角操作達(dá)到一定數(shù)量,會觸發(fā)緩沖區(qū)的頻繁合并和上下文的的頻繁切換,性能的代價(jià)會宏觀地表現(xiàn)在用戶體驗(yàn)上----掉幀。
應(yīng)該如何高性能的給 UIImageView 加圓角
正確的做法是切換到工作線程利用CoreGraphic API生成一個(gè)Off-Screen UIImage,再切換到main thread賦值給UIImageView。
不多 BB, 直接貼代碼:
-(void)cxb_cornerRadiusWithImage:(UIImage *)image cornerRadius:(CGFloat)cornerRadius rectCornerType:(UIRectCorner)rectCornerType {
CGSize size = self.bounds.size;
CGFloat scale = [UIScreen mainScreen].scale;
CGSize cornerRadiu = CGSizeMake(cornerRadius, cornerRadius);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIGraphicsBeginImageContextWithOptions(size, YES, scale);
if (nil == UIGraphicsGetCurrentContext()) {
return;
}
UIBezierPath *cornerPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds byRoundingCorners:rectCornerType cornerRadiu:cornerRadiu];
[cornerPath addClip];
[image drawInRect:self.bounds];
id processedImageRef = (__bridge id _Nullable)(UIGraphicsGetImageFromCurrentImageContext().CGImage);
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
self.layer.contents = processedImageRef;
});
});
}