iOS黑(灰)白化实现方案
admin
2024-03-19 03:11:20
0

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

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

相关内容

热门资讯

linux入门---制作进度条 了解缓冲区 我们首先来看看下面的操作: 我们首先创建了一个文件并在这个文件里面添加了...
C++ 机房预约系统(六):学... 8、 学生模块 8.1 学生子菜单、登录和注销 实现步骤: 在Student.cpp的...
A.机器学习入门算法(三):基... 机器学习算法(三):K近邻(k-nearest neigh...
数字温湿度传感器DHT11模块... 模块实例https://blog.csdn.net/qq_38393591/article/deta...
有限元三角形单元的等效节点力 文章目录前言一、重新复习一下有限元三角形单元的理论1、三角形单元的形函数(Nÿ...
Redis 所有支持的数据结构... Redis 是一种开源的基于键值对存储的 NoSQL 数据库,支持多种数据结构。以下是...
win下pytorch安装—c... 安装目录一、cuda安装1.1、cuda版本选择1.2、下载安装二、cudnn安装三、pytorch...
MySQL基础-多表查询 文章目录MySQL基础-多表查询一、案例及引入1、基础概念2、笛卡尔积的理解二、多表查询的分类1、等...
keil调试专题篇 调试的前提是需要连接调试器比如STLINK。 然后点击菜单或者快捷图标均可进入调试模式。 如果前面...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
IHome主页 - 让你的浏览... 随着互联网的发展,人们越来越离不开浏览器了。每天上班、学习、娱乐,浏览器...
TCP 协议 一、TCP 协议概念 TCP即传输控制协议(Transmission Control ...
营业执照的经营范围有哪些 营业执照的经营范围有哪些 经营范围是指企业可以从事的生产经营与服务项目,是进行公司注册...
C++ 可变体(variant... 一、可变体(variant) 基础用法 Union的问题: 无法知道当前使用的类型是什...
血压计语音芯片,电子医疗设备声... 语音电子血压计是带有语音提示功能的电子血压计,测量前至测量结果全程语音播报࿰...
MySQL OCP888题解0... 文章目录1、原题1.1、英文原题1.2、答案2、题目解析2.1、题干解析2.2、选项解析3、知识点3...
【2023-Pytorch-检... (肆十二想说的一些话)Yolo这个系列我们已经更新了大概一年的时间,现在基本的流程也走走通了,包含数...
实战项目:保险行业用户分类 这里写目录标题1、项目介绍1.1 行业背景1.2 数据介绍2、代码实现导入数据探索数据处理列标签名异...
记录--我在前端干工地(thr... 这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前段时间接触了Th...
43 openEuler搭建A... 文章目录43 openEuler搭建Apache服务器-配置文件说明和管理模块43.1 配置文件说明...