之前看到在一些比较丧的纪念日里,一些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];
第一张网络图片挺好,的确变为黑白的了,但是第二张本地的带透明背景的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
])
对比效果:
这两种办法,在搞非新工程的时候,都需要写一写代码来适配,好在一般的网络图片都是通过列表加载的,不用一个个改,但是需要一个模块一个模块的改。为了更好的节省性能,可以在原始链接追加特定字符将黑白图片用对应的key进行本地的缓存,下次展示的时候可以先在缓存中查找,如果本地已经存在直接拿出来显示,减少对图片进行处理的次数!