关于夜间模式功能实践的一次记录

前言

最近完成app小版本迭代,在论坛看见某个求职贴写着熟练使用Method Swizzling技能,想起15年有阵各种关于iOS黑科技技术贴满天飞,自己也看了些,但是好像实际开发中也确实用的很少(可能确实比较菜🤣)。于是趁着这阵不忙实践一下,第一个就想到夜间模式这个功能用Method Swizzling实现比较合适,相信也是很多开发者第一选择吧,写了个简单demo后发现确实省事儿。

效果图

动图

image

常用的视图控件继承关系图

image

实现思路

本文关于夜间模式功能的实现用到了CategoryMethod SwizzlingNotification三个特性,具体步骤大致是新建UIView分类,重载+(void)load方法,在重载方法里面替换掉UIView两个实例化方法(initWithFrame:initWithCoder:)的,添加接收夜间模式开关的通知,收到通知后根据具体类更新各自UI。

在重载方法load中替换UIView两个实例化方法

1
2
3
4
+ (void)load {
[super load];
[self exchangeMethods];
}

利用Method Swizzling替换实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
+ (void)exchangeInitWithCoderMethod {
Class class = [self class];

SEL originalSelector = @selector(initWithCoder:);
SEL swizzledSelector = @selector(nm_initWithCoder:);

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

+ (void)exchangeInitWithFrameMethod {
Class class = [self class];

SEL originalSelector = @selector(initWithFrame:);
SEL swizzledSelector = @selector(nm_initWithFrame:);

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

在替换方法中注册接收夜间模式开关状态的通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (instancetype)nm_initWithCoder:(NSCoder *)coder{

[[NSNotificationCenter defaultCenter] removeObserver:self name:NightModeChangeNotificationKey object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nm_updateNightModeStatusNotification:) name:NightModeChangeNotificationKey object:nil];

return [self nm_initWithCoder:coder];
}


- (instancetype)nm_initWithFrame:(CGRect)frame {

[[NSNotificationCenter defaultCenter] removeObserver:self name:NightModeChangeNotificationKey object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(nm_updateNightModeStatusNotification:) name:NightModeChangeNotificationKey object:nil];

return [self nm_initWithFrame:frame];
}

外部发送通知,视图自动更新对应状态的UI

1
2
3
4
- (IBAction)lightSwitchAction:(UISwitch *)sender {

[[NSNotificationCenter defaultCenter] postNotificationName:NightModeChangeNotificationKey object:[NSNumber numberWithInteger:sender.isOn]];
}

总结

实现起来不是很难,Method Swizzling实现类似需求确实有奇效,不过具体项目中还没使用过,有什么坑也就不清楚了,看了关于Method Swizzling几篇文章,褒贬不一。个人认为这类黑科技的使用看具体情况吧,团队开发中肯定是要避免滥用的(不要只是为了秀),其他就不发表看法了,毕竟没资质🤣。

其实,关于Method Swizzling的那段代码我是copy的,后面如果有使用这项技术,我还是会回来copy下,毕竟不常用🤣。

相关资源