美文网首页
每日一问06——imageView的圆角优化

每日一问06——imageView的圆角优化

作者: 巫师学徒 | 来源:发表于2017-09-04 11:16 被阅读75次

    基本方法

    使用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.png

    直接裁剪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 圆角和加边框方式总结

    相关文章

      网友评论

          本文标题:每日一问06——imageView的圆角优化

          本文链接:https://www.haomeiwen.com/subject/rstkjxtx.html