关于APP内图片变黑白的思考

之前看到在一些比较丧的纪念日里,一些APP内所有加载的网络图片都变成黑白的了,也看到有的技术群里在讨论这些问题,当时手里几个项目都比较赶,没有过多的思考研究,在iOS项目中这个东西从我的角度将有2种办法,一种是runtime简单粗暴,另一种就是项目中展示图片的控件继承UIImageView然后重写setImage方法,这种思路只能解决静态图片,能解决90%+的需求了,至于动态图要如何处理有待思考。

1. runtime

直接来一个UIImageView的类别利用runtime交换系统的setImage方法:

#import 
#import "UIImage+extend.h"
@implementation UIImageView (box)
+(void)load
{
    Method originalMethod = class_getInstanceMethod([self class], @selector(setImage:));
    Method swizzledMethod = class_getInstanceMethod([self class], @selector(box_setimage:));
    method_exchangeImplementations(originalMethod, swizzledMethod);
}
-(void)box_setimage:(UIImage *)image
{
    [self box_setimage:[image grayImage]];
}
@end

上面代码了用到一个UIImage的类别,用来将图片变为黑白颜色,这个类别在实现的时候把swift版本的也顺带搞了一下:

-- OC版 --
- (UIImage *)grayImage{
    int width = self.size.width;
    int height = self.size.height;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
    CGContextRef context = CGBitmapContextCreate (nil,width,height,8,0,colorSpace,kCGImageAlphaNone);
    CGColorSpaceRelease(colorSpace);
    if (context == NULL) {
        return nil;
    }
    CGContextDrawImage(context,CGRectMake(0, 0, width, height), self.CGImage);
    UIImage *grayImage = [UIImage imageWithCGImage:CGBitmapContextCreateImage(context)];
    CGContextRelease(context);
    return grayImage;
}

-- Swift版本 --
extension UIImage {
    func grayImage() -> UIImage? {
        let width = self.size.width;
        let height = self.size.height;
        let colorSpace:CGColorSpace = CGColorSpaceCreateDeviceGray();
        guard let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: 0) else {
            print("unable to create context")
            return nil
        }
        context.draw(self.cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        let grayImage = UIImage.init(cgImage: context.makeImage()!);
        return grayImage;
    }
}

代码搞好以后,直接写个跑一下看看效果,会明显的发现一些问题:

UIImageView *imgview = [[UIImageView alloc] initWithFrame:CGRectMake(10.0, 80.0, 300.0, 150.0)];
[self.view addSubview:imgview];
 [imgview sd_setImageWithURL:[NSURL URLWithString:@"https://cache.fotoplace.cc/200604/admin/894624B4FF79DBF363C8E62AAA24E83B.png"]];
    
UIImageView *imgviewIcon = [[UIImageView alloc] initWithFrame:CGRectMake(10.0, 250.0, 60.0, 60.0)];
imgviewIcon.image = [UIImage imageNamed:@"slider_tips"];
[self.view addSubview:imgviewIcon];

效果1.png

第一张网络图片挺好,的确变为黑白的了,但是第二张本地的带透明背景的png图标就尴尬了,被变成了黑色底,这明显会不符合产品的期望。如果还想继续用runtime这个方法并不是彻底没办法了,我们可以利用runtime给类别添加属性,然后在box_setimage方法里根据这个属性判断是否需要进行黑白处理。

static NSString *netWorkViewKey = @"netView";
- (void)setNetWorkView:(BOOL)netWorkView {
    objc_setAssociatedObject(self, &netWorkViewKey, @(netWorkView), OBJC_ASSOCIATION_COPY);
}
- (BOOL)netWorkView {
    return [objc_getAssociatedObject(self, &netWorkViewKey) boolValue];
}

上面这段代码就是在类别中给UIImageView添加了一个netWorkView的属性。

2. 重写UIImageView的setImage方法

直接一个继承UIImageView的类:

class BoxImageView: UIImageView {
    open override var image: UIImage?{
        set{
            print("set图")
            super.image = newValue?.grayImage()
        }
        get{
            print("get图")
            return super.image
        }
    }
    /*
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code
    }
    */
}

写个简单的对比代码,代码中使用到了Kingfisher加载的网络图片,无论用的哪个网络框架加载图片,只要是静态图,最终都离不开setImage这个方法:

let imgOriginView:UIImageView = UIImageView()
imgOriginView.contentMode = .scaleAspectFit
self.view.addSubview(imgOriginView)
imgOriginView.snp.makeConstraints { (make) in
  make.top.equalTo(80.0)
  make.left.equalTo(10.0)
  make.size.equalTo(CGSize(width:300,height:150))
}
let url = URL(string: "https://cache.fotoplace.cc/200604/admin/894624B4FF79DBF363C8E62AAA24E83B.png")
imgOriginView.kf.setImage(
  with: url,
  placeholder: nil,
  options: [
    .transition(.fade(1)),
    .cacheOriginalImage
])
        
let imgV:BoxImageView = BoxImageView()
imgV.contentMode = .scaleAspectFit
self.view.addSubview(imgV)
imgV.snp.makeConstraints { (make) in
  make.top.equalTo(220.0)
  make.left.equalTo(10.0)
  make.size.equalTo(CGSize(width:300,height:150))
}
imgV.kf.setImage(
  with: url,
  placeholder: nil,
  options: [
    .transition(.fade(1)),
    .cacheOriginalImage
])

对比效果:


效果2.png

这两种办法,在搞非新工程的时候,都需要写一写代码来适配,好在一般的网络图片都是通过列表加载的,不用一个个改,但是需要一个模块一个模块的改。为了更好的节省性能,可以在原始链接追加特定字符将黑白图片用对应的key进行本地的缓存,下次展示的时候可以先在缓存中查找,如果本地已经存在直接拿出来显示,减少对图片进行处理的次数!

你可能感兴趣的:(关于APP内图片变黑白的思考)