iOS black (gray) whitening implementation scheme

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;
}

iOS Memorial Day Mode

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];

CALayer's filters

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)

CALayer 的 filters
CAFilter

Guess you like

Origin blog.csdn.net/ForeverMyheart/article/details/128187220