清明节要到了,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 < array.count ; i ++) {
SEL sel = NSSelectorFromString(array[i]);
SEL lg_sel = NSSelectorFromString([NSString stringWithFormat:@"lg_%@",array[i]]);
Method originMethod = class_getClassMethod(cls, sel);
Method swizzledMethod = class_getClassMethod(cls, lg_sel);
[self swizzleMethodWithOriginSel:sel oriMethod:originMethod swizzledSel:lg_sel swizzledMethod:swizzledMethod class:cls];
}
之后就是图片,之前的想法是给所有图片都加一层灰色的滤镜,但是在网上找到了改变图片颜色的方法,(其实和滤镜算法差不多,都是改变位图的三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 <UIKit/UIKit.h>
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 APP界面黑白化处理(灰度处理)(为悼念日准备)
iOS 一键哀悼模式(灰度色盲模式)
代码上传到了github 上 如果可以 希望能点个star 😁
传送门