• iOS黑(灰)白化实现方案


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

    需求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

  • 相关阅读:
    全面认识RPA,细述RPA的前世今生
    STM32H7 Azure RTOS
    5、使用 pgAdmin4 图形化创建和连接 PostgreSQL 数据库
    Java 使用 ant.jar 执行 SQL 脚本文件
    Android车载开发小结之sensor,carmanager,carservice串接
    Rust用宏实现参数可变的函数
    基于SSH的网络预约挂号系统的设计与实现
    为什么低代码CRM越来越受欢迎?
    表格分组标签:表格行分组中的隐藏功能
    4.1 设计模式_单例模式
  • 原文地址:https://blog.csdn.net/ForeverMyheart/article/details/128187220