前言
最近在做换肤,但是找了很多方案发现对代码的侵入性都非常大,而且公司项目做了组件化,网上通用的方案虽然也能实现但是代价太大,而且不方便迭代更新。当换肤功能完成大半的时候突发发现一个流弊的换肤框架,SakuraKit我。。。
先附上原文作者的链接:http://www.jianshu.com/p/8930b4496023
作者的 demo本人感觉过于复杂,我这里讲下从简单的使用到源码
使用步骤
- 编写json文件Skin_Tennis.json
{
"PersonalCenter":{
"skinFlagColor":"ffffff",
"skinFlagName":"网球主题",
"backImage":"back_black",
"backTitleColor":"#999999",
"headerBackgroundImage":"me_header_tennis",
"buttonBackgroundImage":"button_background_tennis"
}
}
- 调用
UIImageView *bgImageView = [[UIImageView alloc] init];
bgImageView.contentMode = UIViewContentModeScaleAspectFill;
bgImageView.sakura.image(@"PersonalCenter.headerBackgroundImage");
[self addSubview:bgImageView];
UIButton *button = [[UIButton alloc] init];
button.frame = CGRectMake(0, 0, 100, 44);
button.sakura.backgroundImage(@"PersonalCenter.buttonBackgroundImage", UIControlStateNormal);
button.sakura.titleColor(@"PersonalCenter. backTitleColor", UIControlStateNormal);
- 切换皮肤
[TXSakuraManager shiftSakuraWithName:@"Skin_Tennis" type:TXSakuraTypeMainBundle];
- 恭喜你完成了一个简单的本地换肤
源码?
我们这里以bgImageView.sakura.image(@"PersonalCenter.headerBackgroundImage");
举例,我们点进. sakura
看看
TXSakuraCategoryImplementation(UIImageView, TXSakuraImageView)
懵逼,懵逼就对了,作者怕是搞c++出身的,别怕其实就是个宏而已解析出来就是:
@interface UIImageView (TX)
@property (strong, nonatomic) TXSakuraImageView *sakura;
@end
extern void *kTXSakuraKey;
@implementation UIImageView(TX)
@dynamic sakura;
- (UIImageView *)sakura {
TXSakuraImageView *obj = objc_getAssociatedObject(self, kTXSakuraKey);
if (!obj) {
obj = [TXSakuraImageView sakuraWithOwner:self]
objc_setAssociatedObject(self, kTXSakuraKey, obj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return obj;
}
@end
一个分类而已,并且给当前UIImageView
对象添加了一个sakura
参数,并实现懒加载
. sakura
知道了,那么来看看.image(@"PersonalCenter.headerBackgroundImage");
可以跟参数,那肯定是个block,源码:
- (TXSakuraImageViewBlock)image {
return (TXSakuraImageViewBlock)[super tx_sakuraImageBlockWithName:NSStringFromSelector(_cmd)];
}
别怕我们一层层来看,注意看注释,有的简单方法我就不展开了直接写结果了
- (TXSakuraBlock)tx_sakuraImageBlockWithName:(NSString *)name {
// 实际name=@"image",path=@"PersonalCenter.headerBackgroundImage"
return ^TXSakura *(NSString *path){
return [self send1DMsgObjectWithName:name keyPath:path arg:kTXSakuraArgImage valueBlock:^NSObject *(NSString *keyPath) {
// 这里是通过keyPath取出对应的image
return [TXSakuraManager tx_imageWithPath:keyPath];
}];
};
}
先来看看send1DMsgObjectWithName方法
- (instancetype)send1DMsgObjectWithName:(NSString *)name
keyPath:(NSString *)keyPath
arg:(NSString *)arg
valueBlock:(id(^)(NSString *))valueBlock {
// 这里不过是做了一些拼接生成一个setImage:的sel,并且以sel名为key将keyPaht存到一个全局的字典里面innerSkins1D,最终得到的innerSkins1D是:
/**
{
"setImage:" = {
"com.tingxins.sakura.arg.image" = "PersonalCenter.headerBackgroundImage";
};
}
*/
SEL sel = [self prepareForSkin1DWithName:name keyPath:keyPath argKey:arg];
if (!valueBlock) return [TXSakuraTrash sakuraWithOwner:self];
NSObject *obj = valueBlock(keyPath);
// 给imageView对象send一个sel的方法也就是调用setImage:,参数为valueBlock返回的值,也就是[TXSakuraManager tx_imageWithPath:keyPath];的返回值
[self send1DMsgWithSEL:sel objValue:obj];
return self;
}
看完了设置的方法,那么来看看看切换皮肤的方法(有注释的地方才是关键点)
[TXSakuraManager shiftSakuraWithName:@"Skin_Tennis" type:TXSakuraTypeMainBundle];
+ (BOOL)shiftSakuraWithName:(TXSakuraName *)name type:(TXSakuraType)type {
if (name &&
[name isEqualToString:_currentSakuraName]) return NO;
if (!name) name = kTXSakuraDefault;
switch (type) {
case TXSakuraTypeMainBundle:
_resourcesPath = nil;
// 这里是通过文件名拿到对应的文件路径
_configsFilePath = [self tx_getSakuraConfigsFileBundlePathWithName:name];
break;
case TXSakuraTypeSandbox:{
_resourcesPath = [self tx_getSakuraResourceSandboxPathWithName:name];
_configsFilePath = [self tx_tryGetSakuraConfigsFileSandboxPathWithName:name];
if (!_configsFilePath.length && _resourcesPath.length) {
_configsFilePath = [self tx_getSakuraConfigsFileBundlePathWithName:kTXSakuraDefault];
}
}
break;
default:
break;
}
if (_configsFilePath.length) {
// 这里仅仅把当前选择的皮肤名存到沙盒
[self saveCurrentSakuraInfosWithName:name type:type];
// 这里才是关键,通知!大部分换肤框架都免不了通知
[[NSNotificationCenter defaultCenter] postNotificationName:TXSakuraSkinChangeNotification object:nil];
return YES;
}
#ifdef DEBUG
else {
NSLog(@"resources not exists!");
}
NSLog(@"%@", _configsFilePath);
#endif
return NO;
}
看到通知是不是明白了大概了,对你猜得没错,之前创建的sakura
对象里会接接收这个通知
- (instancetype)initWithOwner:(id)owner {
if (self = [super init]) {
_owner = owner;
_imageRenderingMode = UIImageRenderingModeAlwaysOriginal;
// TXSakura类里面接收通知
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateSakuraSkins) name:TXSakuraSkinChangeNotification object:nil];
}
return self;
}
- (void)updateSakuraSkins {
// 一维参数,self.skins1D其实就是返回上面说的全局的innerSkins1D,还记得里面存的什么吗?
/**
{
"setImage:" = {
"com.tingxins.sakura.arg.image" = "PersonalCenter.headerBackgroundImage";
};
}
*/
[self updateSakuraWith1DSkins:self.skins1D];
// 二维参数,不知道干嘛用,好像没用到,为了拓展?
[self updateSakuraWith2DSkins:self.skins2D];
}
老铁们应该已经猜到接下来该干嘛了吧,updateSakuraWith1DSkins:
这里就不展开了,里面就是拿到innerSkins1D这个全局的字典,根据PersonalCenter.headerBackgroundImage
这个可以取出创建图片名,然后调用setImage:方法
下载?
待更新
最后感谢看完我BB这么多,希望能带给给需要做换肤的小伙伴有一些启发,demo待我稍作修改稍后附上,有更深入的研究后会更新此文
网友评论