换肤-Runtime - (Obj-C)

上一篇文章介绍了通过UIImage分类的方式实现换肤功能,但是此方式有一定的局限性:
需要开发前就考虑到这个功能,如果前期没有考虑到皮肤处理,开发中期或后期产品迭代,需求中添加这个功能,再去添加分类修改代码效率就太低了.这种情况下就可以通过Runtime机制来交换方法.

exchangeImplementations.png

与基本换肤一样,在视图上通过ImageView显示不同的图片演示换肤功能,同时为了演示多界面同步换肤,设置一个TabBarController为根控制器,包含两个子控制器,如图:

界面搭建.png

原理与基础换肤一样,仍然需要添加一个UIImage分类,在Load方法中通过运行时机制交换方法:

  • 首先导入头文件<objc/objc.h>

  • 接下来通过运行时机制交换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 <UIKit/UIKit.h>

@interface UIImage (JSSkin)

// 根据皮肤设置图片
+ (UIImage *)jsImageNamed:(NSString *)name;

// 记录皮肤
+ (void)saveSkinModeWithNight:(BOOL)night;

// 获取皮肤设置
+ (BOOL)isNight;

@end

.m

#import "UIImage+JSSkin.h"
#import <objc/runtime.h>

@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

效果图:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 5,311评论 5 13
  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 3,840评论 7 64
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 137,086评论 19 139
  • 转载:http://yulingtianxia.com/blog/2014/11/05/objective-c-r...
    F麦子阅读 856评论 0 2
  • 1. 前几天去参加了高中同学的婚礼,看着曾经一起上课一起吃饭的朋友都已嫁为人妻了,还是挺感慨的,主要是感慨时间过得...
    文长长阅读 9,543评论 100 335

友情链接更多精彩内容