如何高性能的给 UIImageView 加圆角

肯定不是使用cornerRadius!!! 否则这题目不是显得很弱智。本文会先讲为什么不能这么做,再讲应该如何做

为什么不能使用 cornerRadius

注意审题,题目说的是高性能。那么我们首先要知道使用 cornerRadius 为何会使得性能下降。
首先说一下 GPU 的两种渲染方式,当前屏幕渲染离屏渲染

  • On-Screen Rendering
    意为当前屏幕渲染,指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行。

  • Off-Screen Rendering
    意为离屏渲染,指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。

再来看代码:

imageView.layer.cornerRadius = CGFloat(5);
imageView.layer.masksToBounds = YES;

如上面代码,直接使用 cornerRadius 对 imageView 设置圆角,其渲染方式为 GPU 离屏渲染。而相比于当前屏幕渲染,离屏渲染的代价是很高的,主要体现在两个方面:

1.创建新缓冲区

要想进行离屏渲染,首先要创建一个新的缓冲区。

2.上下文切换

离屏渲染的整个过程,需要多次切换上下文环境:先是从当前屏幕(On-Screen)切换到离屏(Off-Screen),等到离屏渲染结束以后,将离屏缓冲区的渲染结果显示到屏幕上有需要将上下文环境从离屏切换到当前屏幕。而上下文环境的切换是要付出很大代价的。

会触发离屏渲染的操作还有很多,想了解可以自行谷歌或百度

请注意:并非不能使用 cornerRadius 来加圆角,只是如果当前屏幕中这样的圆角操作达到一定数量,会触发缓冲区的频繁合并和上下文的的频繁切换,性能的代价会宏观地表现在用户体验上----掉帧。


应该如何高性能的给 UIImageView 加圆角

正确的做法是切换到工作线程利用CoreGraphic API生成一个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; 
        }); 
    });
}

你可能感兴趣的:(如何高性能的给 UIImageView 加圆角)