美文网首页
SDWebImage网络图片的圆角裁切和不变形处理

SDWebImage网络图片的圆角裁切和不变形处理

作者: 叶孤城1993 | 来源:发表于2020-03-31 19:51 被阅读0次

    挂一张梓沐的图片作为原始素材

    t-io(真的是一个狠牛X的网络通讯框架)的创始人给了我灵感,用梓木的照片做素材(PS:哈哈,已经得到授权,特意给我挑了这张让我写博客),正题开始

    image

    先说网络图片

    - (void)tio_imageUrl:(NSString *)urlStr placeHolderImageName:(NSString *)placeHolderStr radius:(CGFloat)radius {
        NSURL *url;
    
        // 省略
        
        url = [NSURL URLWithString:urlStr];
    
        if (radius != 0.0) {
            // 有圆角,读取圆角的缓存图片
            NSString *cacheurlStr = [urlStr stringByAppendingFormat:@"radius=%.1f",radius];
            
            UIImage *cacheImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:cacheurlStr];
            if (cacheImage) {
                self.image = cacheImage;
            }
            else {
                [self sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:placeHolderStr] completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
                    if (!error) {
                        // 开始裁剪处理
                        UIImage *radiusImage = [self.image imageWithCornerRadius:radius size:self.frame.size];
                        self.image = radiusImage;
                        [[SDImageCache sharedImageCache] storeImage:radiusImage forKey:cacheurlStr completion:nil];
                        //清除原有非圆角图片缓存
                        [[SDImageCache sharedImageCache] removeImageForKey:urlStr withCompletion:nil];
                    }
                }];
            }
        }
        else {
            [self sd_setImageWithURL:url placeholderImage:[UIImage imageNamed:placeHolderStr] completed:nil];
        }
    }
    
    1. 先根据图片URLString拼接圆角参数到SD查询有无图片
    2. 若有直接从SDImageCache读取给 self.image显示
    3. 若没有开始SD下载、下载后裁剪、self.image显示、向SDImageCache存入裁剪后的图片,key是图片URLString拼接圆角参数

    其次是圆角裁剪

    大家一般不会采取

    imageView.layer.cornerRadius = 20;
    imageView.layer.masksToBounds = YES;
    

    这样造成离屏渲染,特别是在列表中,当快速滑动列表,生理上的卡顿就会迸发

    我使用UIGraphics进行裁剪圆角

    - (instancetype)imageWithCornerRadius:(CGFloat)cornerRadius size:(CGSize)newSize
    {
        UIImage *originImage = self;
        
        // 开始裁切圆角
        CGRect bounds = CGRectMake(0, 0, newSize.width, newSize.height);
        UIGraphicsBeginImageContextWithOptions(newSize, NO, UIScreen.mainScreen.scale);
        CGContextRef context = UIGraphicsGetCurrentContext();
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:bounds
                                                        cornerRadius:cornerRadius];
        CGContextAddPath(context, path.CGPath);
        CGContextClip(context);
        [originImage drawInRect:bounds];
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return image;
    }
    

    接下来,对于填充模式UIViewContentModeScaleAspectFill

    当我们使用上述方法进行裁切圆角,然后直接给imageView显示,会出现图片变形拉伸或者压缩

    但是,有的朋友会采用如下方式,对控件imageView的contentMode进行设置,并裁剪多余的地方

    imageView.contentMode = UIViewContentModeScaleAspectFill;
    imageView.clipsToBounds = YES;
    

    理想很好 现实如下

    image

    很明显,从“变胖”的梓沐就知道,这段代码并没有起什么作用,但这是为什么呢??又从什么时候开始“变胖”的??

    我们一步步探寻

    首先,我们看看开始裁切前的原始图片

    image

    断点处肯定OK,毕竟还没开始切

    再打个断点看看切完圆角后的图片

    梓沐竟然变胖了!


    image

    “变胖”的问题出在

    [originImage drawInRect:bounds];
    

    将梓沐原图一个像素不落的全部硬生生的在新的尺寸内绘制,除非原图宽高比与目标尺寸newSize宽高比一致,否则就是压缩或拉伸。

    那么等imageView拿到的图片就已经是变形的、而且和imageView尺寸一样的图片,所以也没有多余的部分值得clipsToBounds去裁剪,所以contentModel和clipsToBounds无效。

    所以我们需要在裁切圆角前先行做一个处理:转成目标尺寸同比例的图片

    image

    scaleImage:方法如下

    - (UIImage *)scaleImage:(CGSize)newSize
    {
        CGFloat width = self.size.width;
        CGFloat height = self.size.height;
        
        CGFloat scale = newSize.width / newSize.height;
        CGFloat imageScale = width / height;
        
        if (imageScale > scale) {
            // 以高为准
            width = height * scale;
        } else if (imageScale < scale) {
            // 以宽为准
            height = width / scale;
        } else {
            // 正常比例
        }
        
        // 中心放大
        CGRect frame = CGRectMake((self.size.width - width) * 0.5, (self.size.height - height) * 0.5, width, height);
        
        CGImageRef imageRef = [self CGImage];
        imageRef = CGImageCreateWithImageInRect(imageRef, frame);
        UIImage *image = [UIImage imageWithCGImage:imageRef];
        
        
        return image;
    }
    

    官方吐槽:这个代码显然是没有优化,那么多判断完全可以用宏解决,顺便也还能扩展各种contentMode的处理,这个后续会讲,也很简单

    这是我们要的最终效果

    image

    处理前后进行对比

    处理前


    image

    处理后


    image

    我只实现了UIViewContentModeScaleAspectFill的效果
    其他效果做法一样,如上述所说,在scaleImage方法内可以扩展,实现裁剪的居上、居左、居右等,也可以添加上左下右的裁剪偏移量等等

    思路都很简单,没有什么特别,工作这么多年,一点点写吧,之前也没养成写博客的习惯

    文末小源码:GitHub

    相关文章

      网友评论

          本文标题:SDWebImage网络图片的圆角裁切和不变形处理

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