美文网首页iOS新知识
关于APP内图片变黑白的思考

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

作者: BoxJing | 来源:发表于2020-06-11 17:26 被阅读0次

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

    1. runtime

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

    #import <objc/runtime.h>
    #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内图片变黑白的思考

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