上一篇文章介绍了通过UIImage分类的方式实现换肤功能,但是此方式有一定的局限性:
需要开发前就考虑到这个功能,如果前期没有考虑到皮肤处理,开发中期或后期产品迭代,需求中添加这个功能,再去添加分类修改代码效率就太低了.这种情况下就可以通过Runtime机制来交换方法.
与基本换肤一样,在视图上通过ImageView显示不同的图片演示换肤功能,同时为了演示多界面同步换肤,设置一个TabBarController为根控制器,包含两个子控制器,如图:
原理与基础换肤一样,仍然需要添加一个UIImage分类,在Load方法中通过运行时机制交换方法:
首先导入头文件
接下来通过运行时机制交换imageNamed和jsImageNamed方法
// 1. 获取对应交换的方法
Method method1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method method2 = class_getClassMethod([UIImage class], @selector(jsImageNamed:));
// 2. 交换方法
method_exchangeImplementations(method1, method2);
- 外界直接使用系统方法imageNamed就可以了,运行时已经交换了两个方法的实现部分
#######需要注意的地方:
1.使用运行时机制交换方法,在App整个生命周期都会交换
2.使用交换方法后,注意死循环问题:
在自定义的方法中,调用了系统imageNamed方法,就会造成死循环(运行时交换的是两个方法的实现部分,当外界调用imageNamed的时候,实际执行的JSImageNamed的实现部分,这样方法内部调用了自己),但这里又要使用系统方法imageNamed,所以要换成自定义的jsImageNamed方法就可以了
// 自定义方法,根据当前皮肤设置图片
+ (UIImage *)jsImageNamed:(NSString *)name{
if (isNight) { // 夜间模式
name = [NSString stringWithFormat:@"%@_night",name];
}
return [UIImage jsImageNamed:name];
}
完整代码:
.h
#import
@interface UIImage (JSSkin)
// 根据皮肤设置图片
+ (UIImage *)jsImageNamed:(NSString *)name;
// 记录皮肤
+ (void)saveSkinModeWithNight:(BOOL)night;
// 获取皮肤设置
+ (BOOL)isNight;
@end
.m
#import "UIImage+JSSkin.h"
#import
@implementation UIImage (JSSkin)
// 夜间模式标识
static bool isNight;
+ (void)load{
// 获取偏好设置中的皮肤模式
isNight = [[NSUserDefaults standardUserDefaults] boolForKey:@"isNight"];
// 使用运行时机制交换方法 一旦交换,在App整个生命周期都会交换
// 1. 获取对应交换的方法
Method method1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
Method method2 = class_getClassMethod([UIImage class], @selector(jsImageNamed:));
// 2. 交换方法
method_exchangeImplementations(method1, method2);
}
// 自定义方法,根据当前皮肤设置图片
+ (UIImage *)jsImageNamed:(NSString *)name{
if (isNight) { // 夜间模式
name = [NSString stringWithFormat:@"%@_night",name];
}
return [UIImage jsImageNamed:name];
}
+ (void)saveSkinModeWithNight:(BOOL)night{
// 赋值,记录当前皮肤状态
isNight = night;
// 本地记录状态(偏好设置)
[[NSUserDefaults standardUserDefaults] setBool:isNight forKey:@"isNight"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
+ (BOOL)isNight{
// 返回当前皮肤状态
return isNight;
}
@end
外界调用:
控制器1
#import "PageOneViewController.h"
#import "UIImage+JSSkin.h"
@interface PageOneViewController ()
@property (weak, nonatomic) IBOutlet UIImageView *imageView_1;
@property (weak, nonatomic) IBOutlet UIImageView *imageView_2;
@property (weak, nonatomic) IBOutlet UISwitch *nightModeSwitch;
@end
@implementation PageOneViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 获取当前皮肤模式,同步Switch状态
self.nightModeSwitch.on = [UIImage isNight];
// 设置图片
self.imageView_1.image = [UIImage imageNamed:@"baby"];
self.imageView_2.image = [UIImage imageNamed:@"girl"];
}
// Switch开关点击事件
- (IBAction)nightModeSwitchClick:(UISwitch *)sender {
// 本地化存储(偏好设置)
[UIImage saveSkinModeWithNight:sender.isOn];
// 设置图片
self.imageView_1.image = [UIImage imageNamed:@"baby"];
self.imageView_2.image = [UIImage imageNamed:@"girl"];
}
@end
控制器2
#import "PageTwoViewController.h"
#import "UIImage+JSSkin.h"
@interface PageTwoViewController ()
@property (strong, nonatomic) IBOutlet UIImageView *imageView_3;
@end
@implementation PageTwoViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
// 设置图片
self.imageView_3.image = [UIImage imageNamed:@"girl"];
}
@end
效果图: