Depending on the business, there are generally two requirements for products:
Requirement 1: Set everything to black and white
Requirement 2: Set a certain interface to black and white
Rough implementation:
Option One:
The server sends all black (gray) pictures, and the font color supports dynamic delivery
. This is okay if there is only one interface, but if you replace all pictures, the workload is too much
Option II:
It roughly involves: image, color of UILabel, color of UIButton, webView, Video, etc.
For image, UIImageView is generally used to display, therefore, use the method exchange in runtime to let the setImage: method take its own.
Then add a filter to the picture in the private method
+ (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 { //Whether to black and white, 1 means open 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 grayed out - classification
WKWebView+blackWhiteModel.m file:
#import "WKWebView+blackWhiteModel.h"
#import <objc/runtime.h>
@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 interface black and white processing (grayscale processing) (prepared for the memorial day)
There is a problem with the above solution, because it is a replacement init method, which will cause the webView before the switch is 0 to be colored, and the webView after the switch is 1 to be gray. Therefore, be sure to confirm that the result of requesting whether to switch is before creating the webView
. or later
H5 grayed out—single
Can be grayed out for a single H5
Same as the js code above:
BOOL isOpenWhiteBlackModel = [[NSUserDefaults standardUserDefaults] boolForKey:@"kIsShowBlackWhiteModel"];
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
WKUserContentController *userController = [[
WKUserContentController alloc]
init]; { //Mourning day mode replaces the overall theme color of 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;
}
For other classifications of UILabel, UIButton, etc., please refer to:
https://github.com/GeLeis/App_NoirDemo
third solution:
Image processing is similar to Solution 2
, but the colors of Label, View, etc. are no longer classified one by one, and the classification of Color is directly modified
+ (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 { // If it is monochrome mode (black and white mode), the average r, g, b value //whether black and white, 1 means open BOOL isOpenWhiteBlackModel = [[NSUserDefaults standardUserDefaults] boolForKey:@"kIsShowBlackWhiteModel"]; if (isOpenWhiteBlackModel) { //r, g, b weight adjustment to prevent occurrence, 1 0 0, 0 1 0 ,0 0 1 the same result //0.2126, 0.7152, 0.0722 These three are calculated according to the strength of the human eye's perception of the three colors r, g, and 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 implements app black and white mode
Option four:
No longer through the runtime method, but directly add a gray filter to the view
//Get RGBA color value
CGFloat r,g,b,a;
[[UIColor lightGrayColor] getRed:&r green:&g blue:&b alpha:&a]; //
Create filter
id cls = NSClassFromString(@"CAFilter");
id filter = [cls filterWithName:@"colorMonochrome"];
//Set filter parameters
[filter setValue:@[@(r),@(g),@(b),@(a)] forKey:@"inputColor "];
[filter setValue:@(0) forKey:@"inputBias"];
[filter setValue:@(1) forKey:@"inputAmount"];
//Set to window
self.window.layer.filters = [NSArray arrayWithObject:filter];
The values of r, g, b, and a can be modified directly instead of [UIColor lightGrayColor]
If it’s just a certain controller A, A.view.layer.filters = [NSArray arrayWithObject:filter];
just set it
The iOS App page is grayed out
Of course, there are other filters that can be used
id cls = NSClassFromString(@"CAFilter");
id filter = [cls filterWithName:@"colorSaturate"];
[filter setValue:@(0) forKey:@"inputAmount"];
//设置给window
self.window.layer.filters = [NSArray arrayWithObject:filter];
CAFilter is a private method of Apple and may be rejected, so this method is not used
final approach
Add a view that does not receive click events
@interface ZRLandlordHPGrayView : UIView
@end
@implementation ZRLandlordHPGrayView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
return nil;
}
@end
Then, in the interface that needs to display black and white mode, add the following method:
- (void)showGrayViewWithSuperView:(UIView *)superView
{ //This method is used to store whether it is black and white mode BOOL isOpenWhiteBlackModel = [[NSUserDefaults standardUserDefaults] boolForKey:@"kIsShowBlackWhiteModel"]; if (isOpenWhiteBlackModel) { if (@available( iOS 12.0, *)) {//Only support ZRLandlordHPGrayView 12 and above *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];
}
}
}
This method only supports 12 and above.
After looking at our app, there are basically very few below 12, so I finally chose this method
Other reference articles:
iOS interface graying solution discussion
iOS mourning day mode
using black magic on iOS to achieve a one-click global picture graying solution
iOS APP interface black and white processing (grayscale processing) (prepared for the mourning day)