前言:
迫于部门周会要做主持,得拿出点什么东西分享。一时间也想不到分享些什么好。
想想自己从未做过 iOS 主题动态切换这个需求,又觉得这个需求好像蛮有挑战的。
所以就有了今天这个文章。😁😁😁
思路是这样的:
第一步
首先考虑的是输入方式,既我们怎样配置主题文件?
我想到了3种方式:
1)直接代码组织;
先来直接工程中用类来配置主题包。
我是采用通过 AGThemePack 主题包类,派生子类的方式来做,每一个子类就是独立的主题包。
2)文件,如JSON 文件或 .plist 文件;
那么对于文件,这个也好处理,同样派生 AGThemePack 的子类,然后在内部加载文件并解析数据就好了。
3)网络数据;
对于网络数据也是同上,派生 AGThemePack 的子类,在内部解析网络数据。
第二步
接着考虑如何输出,既我们的主题更新时,如何让界面响应?
对这种需求用观察者的方式做再好不过了。
相对于通知这种观察者,我选择了Block 这种方式。
而且用NSMapTable 还可以做到视图销毁后,自动移除Block。
第三步
我们把输入输出都考虑好后,就要考虑数据的流动了。
这一步要确定使用哪一种数据结构,和协助类的组织方式。
在这里,曾考虑过使用一个 Task 类来管理 Block 的生命周期,后来简化掉了。
所以最终是使用 AGThemePackBox 主题箱的概念来管理所有的主题包,每个主题包类注册到主题箱中。
AGThemePackBox 就相当于一个主题容器和主题状态管理器。
那么观察者是谁呢?是 AGThemeManager (很俗的名字😂😂😂)。
它内部有个容器管理所有订阅者的Block,当订阅者销毁后,就丢弃对应的Block。
当主题更换时,调用所有订阅者的Block,以达到界面刷新。
结尾:(总结最近悟出的方法论)
- 首先思考:输入与输出的方式与效果;
- 然后思考:从输入到输出的数据流动与处理方式;
- 接着思考:这种数据流动与处理方式对应使用哪种数据结构好;
- 最后,把以上的过程方式通过设计各种类与数据结构连结起来。
使用方式:AGThemeManager @GitHub
1,通过派生子类生成主题包,有3中方式生成主题包
- 工程中
派生 AGThemePack 的子类,重写初始化方法 -initWithPackName: 并配置主题元素;
每个子类就是不同的主题包
- 本地文件
派生 AGThemePack 的子类,重写初始化方法 -initWithContentOfFile: 并配置主题元素;
- 网络
派生 AGThemePack 的子类,重写初始化方法 -initWithThemePackData: 并配置主题元素;
// 参考 项目中 Demo/CustomTheme 下的自定义类
2,在UI类中使用主题包
- 调用 -ag_setupAndExecuteThemeUsingBlock: 方法,添加设置Block并执行;
- 需要注意的是,在Block 中注意循环引用问题;
- 其实不需要手动移除设置Block,在类dealloc 后,会自动移除Block;
// 根据主题,配置好并执行
__weak typeof(self) weakSelf = self;
[self ag_setupAndExecuteThemeUsingBlock:^(NSString * _Nonnull theme, AGThemePack * _Nonnull pack) {
__strong typeof(weakSelf) self = weakSelf;
if ( nil == self ) return;
// ...
NSString *imageName = pack[kAGThemePackHomeCellIconImageName];
self.imageView.image = [UIImage imageNamed:imageName];
self.textLabel.font = pack[kAGThemePackHomeCellContentTextFont];
self.textLabel.textColor = pack[kAGThemePackHomeCellContentTextColor];
}];
3,初始化配置
- 在 AppDelegate 中,初始化视图界面之前配置好要使用的主题;
// 配置主题
AGThemePackBox *themePackBox = [AGThemePackBox newWithCurrentTheme:kAGOrangeThemePack];
[themePackBox ag_registerThemePack:[AGOrangeThemePack newWithPackName:kAGOrangeThemePack]];
[themePackBox ag_registerThemePack:[AGPurpleThemePack newWithPackName:kAGPurpleThemePack]];
[themePackBox ag_registerThemePack:[AGBlueThemePack newWithPackName:kAGBlueThemePack]];
[AGThemeManager sharedInstance].themePackBox = themePackBox;
// 打开调试日志
[AGThemeManager sharedInstance].openLog = YES;