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

·  阅读 2215

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

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

先说网络图片

- (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;
复制代码

理想很好 现实如下

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

我们一步步探寻

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

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

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

梓沐竟然变胖了!

“变胖”的问题出在

[originImage drawInRect:bounds];
复制代码

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

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

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

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的处理,这个后续会讲,也很简单

这是我们要的最终效果

处理前后进行对比

处理前

处理后

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

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

文末小源码:GitHub

分类:
iOS
标签:
分类:
iOS
标签: