iOS 悼念日模式

时间:2021-7-21 作者:qvyue

清明节要到了,app要上悼念日模式,整体的风格要求变成灰色,之前看过腾讯新闻实现过类似的功能,这回终于要在自己app上实现了,而且要做成根据接口控制


安卓有系统设置可以几行代码就实现

iOS 这里没有类似的系统api 就只能自己想办法实现了,思路大概就是俩种

  • 根据接口开关,替换全部控件的颜色

  • 使用runtime替换所有控件的颜色赋值方法

第一种工作量大,而且不现实,不说iOS原生控件,WKwebView的颜色也不能控制,于是直接开始第二种思路


iOS UIKIT中常用控件为 button、imageView、lable、view等,设置颜色 大多是setBackgroundColor、textColor等方法,直接使用runtime 的方法替换来实现

方法替换代码

    Class class = [self class];
    
    SEL originalSelector = @selector(setBackgroundColor:);
    SEL swizzledSelector = @selector(lh_setButtonBackgroundColor:);
    
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    if (didAddMethod) {
        class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }

tips

创建常用控件的分类,获取控件的实例方法时使用以下api

 Class class = [self class];
    
Method originalMethod = class_getInstanceMethod(class, @selector(setBackgroundColor:));

获取控件的类方法时使用以下api

Class cls = object_getClass(self);

Method originMethod = class_getClassMethod(cls, @selector(imageNamed:));

控件替换之后,再替换颜色赋值方法,创建color分类,从根本上把颜色替换成自己想要的颜色

Class cls = object_getClass(self);
    
    //将系统提供的colorWithRed:green:blue:alpha:替换掉
    Method originMethod = class_getClassMethod(cls, @selector(colorWithRed:green:blue:alpha:));
    Method swizzledMethod = class_getClassMethod(cls, @selector(lg_colorWithRed:green:blue:alpha:));
    [self swizzleMethodWithOriginSel:@selector(colorWithRed:green:blue:alpha:) oriMethod:originMethod swizzledSel:@selector(lg_colorWithRed:green:blue:alpha:) swizzledMethod:swizzledMethod class:cls];
    
    //将系统提供的colors也替换掉
    NSArray *array = [NSArray arrayWithObjects:@"redColor",@"greenColor",@"blueColor",@"cyanColor",@"yellowColor",@"magentaColor",@"orangeColor",@"purpleColor",@"brownColor",@"systemBlueColor",@"systemGreenColor", nil];
    
    for (int i = 0; i 

之后就是图片,之前的想法是给所有图片都加一层灰色的滤镜,但是在网上找到了改变图片颜色的方法,(其实和滤镜算法差不多,都是改变位图的三rgb三元素)然后直接替换imageView的setImage:方法,返回灰色处理过后的图片

 //交换方法
    Class class = [self class];
    
    SEL originalSelector = @selector(setImage:);
    SEL swizzledSelector = @selector(swizzled_setImage:);
    
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    if (didAddMethod) {
        class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }

替换图片的时候出现了一个问题 ,底部tabbarItem 上的图片变成了黑色,于是在替换的image 方法上使用imageWithRenderingMode模式,展示原样的图片

imageWithRenderingMode

再之后就是webView里面内容的置灰,在WKwebview里面注入js代码
,但是由于我的WKwebview里面写了跟js交互的代码,所以这段代码没有用runtime卸载分类里面 ,而是直接写在了我的WKWebViewController里面的WKWebView初始化里面

    NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    NSString * change = [userDefault objectForKey:@"CHANGEGRAYMODEFORAPP"];
        
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    WKUserContentController *userController = [[WKUserContentController alloc] init];
    configuration.userContentController = userController;
    
    if ([safeString(change) isEqualToString:@"1"]) {
        //悼念日模式 替换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;
}

再之后就是导航栏的颜色变换
由于项目是了oc与swift混编,使用了多个navagationVc,然而在swift 的导航栏中 runtime 的颜色变换 并没有生效
于是导航栏的颜色也是使用本地开关设置的

NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
    NSString * change = [userDefault objectForKey:@"CHANGEGRAYMODEFORAPP"];
    if ([safeString(change) isEqualToString:@"1"]) {
        //悼念日模式 变换导航栏颜色
        self.navigationBar.tintColor = [UIColor lightGrayColor];
    }

最后就是要使用接口来控制是否展示悼念日模式,于是把接口写在了最前面的didFinishLaunchingWithOptions方法里面
到这里 基本上就算是完成了,

tips

使用storyBoard画的页面 会有imageView颜色不变换问题,解决办法是把控件的属性拉出来 ,手动再赋值一遍

 self.bgView.image = UIImage.init(named: "Icon_Login")

由于需要显示控制,于是在每个分类创建了个类方法

#import 

NS_ASSUME_NONNULL_BEGIN

@interface UIColor (LhGray)

+(void)lh_colorSwizzldColorMethedWith:(BOOL)changeGray;

@end

NS_ASSUME_NONNULL_END

之后再接口中判断是否显示

//悼念日模式开启
                [UIImageView lh_imageViewSwizzldMethedWith:true];
                //[WKWebView lh_WKWebViewWizzldMethedWith:true];
                [UIColor lh_colorSwizzldColorMethedWith:true];
                [UIButton lh_buttonSwizzldMethedWith:true];
                [UINavigationBar lh_navigationBarSwizzldMethedWith:true];
                NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
                [userDefault setObject:@"1" forKey:@"CHANGEGRAYMODEFORAPP"];
                [userDefault synchronize];



效果如下

iOS 悼念日模式
B8FAE3733E95F0A1682BDEA819B9F934.png

最后的最后感谢俩位作者提供的文章
iOS APP界面黑白化处理(灰度处理)(为悼念日准备)
iOS 一键哀悼模式(灰度色盲模式)

代码上传到了github 上 如果可以 希望能点个star 😁
传送门

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。