领导让调研下黑(灰)白化实现方案,自己调研了两天,根据网上资料,做下记录 只是学习过程中的记录,还是写作者牛逼,参考资料会附在文章中

根据业务不一样,大致产品会有两种需求:

需求1:全部设置为黑白色需求2:某个界面设置为黑白色

大致的实现方案:

方案一:

服务端下发所有黑(灰)图片,字体颜色支持动态下发 这个,如果是只有某个界面还好,如果是全量替换图片,工作量太大

方案二:

里面大致涉及到:image、UILabel的color、UIButton的Color、webView、Video等等

对于image,一般都是使用UIImageView去显示,因此,利用runtime里面的方法交换,让setImage:方法走自己的。 然后在私有方法里面实现对图片添加滤镜

+ (void)load {

Method customMethod = class_getInstanceMethod([self class], @selector(setImage:));

Method originMethod = class_getInstanceMethod([self class], @selector(gl_setImage:));

method_exchangeImplementations(customMethod, originMethod);//方法交换

}

- (void)gl_setImage:(UIImage *)image {

//是否黑白化,1表示开启

BOOL isOpenWhiteBlackModel = [[NSUserDefaults standardUserDefaults] boolForKey:@"kIsShowBlackWhiteModel"];

if (isOpenWhiteBlackModel == 1) {

[self gl_setImage:[self gl_grayImage:image]];

} else {

[self gl_setImage:image];

}

}

- (UIImage *)gl_grayImage:(UIImage *)image {

//UIKBSplitImageView是为了键盘

if (image == nil || [self.superview isKindOfClass:NSClassFromString(@"UIKBSplitImageView")]) {

return image;

}

//滤镜处理

//CIPhotoEffectNoir黑白

//CIPhotoEffectMono单色

NSString *filterName = @"CIPhotoEffectMono";

CIFilter *filter = [CIFilter filterWithName:filterName];

CIImage *inputImage = [[CIImage alloc] initWithImage:image];

[filter setValue:inputImage forKey:kCIInputImageKey];

CGImageRef cgImage = [self.filterContext createCGImage:filter.outputImage fromRect:[inputImage extent]];

UIImage *resultImg = [UIImage imageWithCGImage:cgImage];

CGImageRelease(cgImage);

return resultImg;

}

- (CIContext *)filterContext {

CIContext *con = objc_getAssociatedObject(self, @selector(filterContext));

if (!con) {

con = [[CIContext alloc] initWithOptions:nil];

self.filterContext = con;

}

return con;

}

- (void)setFilterContext:(CIContext *)filterContext {

objc_setAssociatedObject(self, @selector(filterContext), filterContext, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

H5变灰—分类

WKWebView+blackWhiteModel.m文件:

#import "WKWebView+blackWhiteModel.h"

#import

@implementation WKWebView (blackWhiteModel)

+ (void)load {

Method customMethod = class_getInstanceMethod([self class], @selector(gl_initWithFrame:configuration:));

Method originMethod = class_getInstanceMethod([self class], @selector(initWithFrame:configuration:));

method_exchangeImplementations(customMethod, originMethod);//方法交换

}

- (instancetype)gl_initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration

{

BOOL isOpenWhiteBlackModel = [[NSUserDefaults standardUserDefaults] boolForKey:@"kIsShowBlackWhiteModel"];

if (isOpenWhiteBlackModel) {

// js脚本

NSString *jScript = @"var filter = '-webkit-filter:grayscale(100%);-moz-filter:grayscale(100%); -ms-filter:grayscale(100%); -o-filter:grayscale(100%) filter:grayscale(100%);';document.getElementsByTagName('html')[0].style.filter = 'grayscale(100%)';";

// 注入

WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];

WKUserContentController *wkUController = [[WKUserContentController alloc] init];

[wkUController addUserScript:wkUScript];

// 配置对象

WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init];

wkWebConfig.userContentController = wkUController;

configuration = wkWebConfig;

WKWebView *webView = [self gl_initWithFrame:frame configuration:configuration];

return webView;

}

return [self gl_initWithFrame:frame configuration:configuration];

}

@end

iOS APP界面黑白化处理(灰度处理)(为悼念日准备)

上述方案有个问题,因为是替换的init方法,会导致在开关为0之前的webView都是彩色、开关为1之后的webView都是灰色 因此,务必确认,请求是否开关的结果在创建webView之前,还是之后

H5变灰—单个

可以针对单个的H5做变灰处理

跟上面的js代码都一样:

BOOL isOpenWhiteBlackModel = [[NSUserDefaults standardUserDefaults] boolForKey:@"kIsShowBlackWhiteModel"];

WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];

WKUserContentController *userController = [[WKUserContentController alloc] init];

configuration.userContentController = userController;

if (isOpenWhiteBlackModel) {

//悼念日模式 替换wkView整体主题色

[userController addUserScript:[self getJsStr]];

}

-(WKUserScript *)getJsStr{

NSString *jScript = @"var filter = '-webkit-filter:grayscale(100%);-moz-filter:grayscale(100%); -ms-filter:grayscale(100%); -o-filter:grayscale(100%) filter:grayscale(100%);';document.getElementsByTagName('html')[0].style.filter = 'grayscale(100%)';";

// 注入

WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];

return wkUScript;

}

iOS 悼念日模式

其他UILabel、UIButton等的分类可参考:

https://github.com/GeLeis/App_NoirDemo

方案三:

图片处理与方案二类似 而Label、View等的color不再一个一个做分类处理,直接修改Color的分类

+ (void)load {

//关键方法交换

Method customMethod = class_getClassMethod([self class], @selector(gl_colorWithRed:green:blue:alpha:));

Method originMethod = class_getClassMethod([self class], @selector(colorWithRed:green:blue:alpha:));

method_exchangeImplementations(customMethod, originMethod);//方法交换

}

+ (UIColor *)gl_colorWithRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha {

//如果是单色模式(黑白模式),则平均r、g、b值

//是否黑白化,1表示开启

BOOL isOpenWhiteBlackModel = [[NSUserDefaults standardUserDefaults] boolForKey:@"kIsShowBlackWhiteModel"];

if (isOpenWhiteBlackModel) {

//r,g,b权重调整,防止出现,1 0 0 、0 1 0,0 0 1同样的结果

//0.2126,0.7152,0.0722 这三个是根据人眼对r,g,b三个颜色面感的强弱算出来的

CGFloat brightness = (red * 0.2126 + 0.7152 * green + 0.0722 * blue);

return [self gl_colorWithRed:brightness green:brightness blue:brightness alpha:alpha];

}

return [self gl_colorWithRed:red green:green blue:blue alpha:alpha];

}

iOS 实现app黑白模式

方案四:

不再通过runtime的方法,而是直接为view添加灰色滤镜

//获取RGBA颜色数值

CGFloat r,g,b,a;

[[UIColor lightGrayColor] getRed:&r green:&g blue:&b alpha:&a];

//创建滤镜

id cls = NSClassFromString(@"CAFilter");

id filter = [cls filterWithName:@"colorMonochrome"];

//设置滤镜参数

[filter setValue:@[@(r),@(g),@(b),@(a)] forKey:@"inputColor"];

[filter setValue:@(0) forKey:@"inputBias"];

[filter setValue:@(1) forKey:@"inputAmount"];

//设置给window

self.window.layer.filters = [NSArray arrayWithObject:filter];

r, g, b, a的值都可以直接修改,而非必须是[UIColor lightGrayColor]

如果只是某个控制器A,则设置A.view.layer.filters = [NSArray arrayWithObject:filter];即可

iOS App页面置灰

当然,还有其他filter可以供使用

id cls = NSClassFromString(@"CAFilter");

id filter = [cls filterWithName:@"colorSaturate"];

[filter setValue:@(0) forKey:@"inputAmount"];

//设置给window

self.window.layer.filters = [NSArray arrayWithObject:filter];

CALayer 的 filters

CAFilter为苹果私有方法,有被拒可能,因此,没有用这个方法

最终做法

添加一个view,里面不接收点击事件

@interface ZRLandlordHPGrayView : UIView

@end

@implementation ZRLandlordHPGrayView

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event

{

return nil;

}

@end

然后,在需要显示黑白模式的界面,添加如下方法:

- (void)showGrayViewWithSuperView:(UIView *)superView

{

//该方法是用来存储是否为黑白模式

BOOL isOpenWhiteBlackModel = [[NSUserDefaults standardUserDefaults] boolForKey:@"kIsShowBlackWhiteModel"];

if (isOpenWhiteBlackModel) {

if (@available(iOS 12.0, *)) {//只支持12及以上

ZRLandlordHPGrayView *overlay = [[ZRLandlordHPGrayView alloc] initWithFrame:superView.bounds];

overlay.userInteractionEnabled = NO;

overlay.translatesAutoresizingMaskIntoConstraints = false;

overlay.backgroundColor = [UIColor grayColor];

overlay.layer.compositingFilter = @"saturationBlendMode";

[superView addSubview:overlay];

[superView bringSubviewToFront:overlay];

}

}

}

该做法只支持12及12以上 看了下我们的app,12以下的基本上凤毛麟角,所以最终选择了这种方法

其他参考文章: iOS界面置灰方案讨论 iOS 悼念日模式 在iOS使用黑魔法实现一键全局图片变灰白的一种方案 iOS APP界面黑白化处理(灰度处理)(为悼念日准备)

CALayer 的 filters CAFilter

精彩文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: