前言
最近在做換膚煤篙,但是找了很多方案發(fā)現(xiàn)對代碼的侵入性都非常大萌壳,而且公司項目做了組件化场梆,網(wǎng)上通用的方案雖然也能實現(xiàn)但是代價太大盔几,而且不方便迭代更新晴弃。當(dāng)換膚功能完成大半的時候突發(fā)發(fā)現(xiàn)一個流弊的換膚框架,SakuraKit我逊拍。上鞠。。
先附上原文作者的鏈接:http://www.reibang.com/p/8930b4496023
作者的 demo本人感覺過于復(fù)雜芯丧,我這里講下從簡單的使用到源碼
使用步驟
- 編寫json文件Skin_Tennis.json
{
"PersonalCenter":{
"skinFlagColor":"ffffff",
"skinFlagName":"網(wǎng)球主題",
"backImage":"back_black",
"backTitleColor":"#999999",
"headerBackgroundImage":"me_header_tennis",
"buttonBackgroundImage":"button_background_tennis"
}
}
- 調(diào)用
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];
- 恭喜你完成了一個簡單的本地?fù)Q膚
源碼芍阎?
我們這里以bgImageView.sakura.image(@"PersonalCenter.headerBackgroundImage");
舉例,我們點進(jìn). 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
一個分類而已骗露,并且給當(dāng)前UIImageView
對象添加了一個sakura
參數(shù)寿冕,并實現(xiàn)懶加載
. sakura
知道了,那么來看看.image(@"PersonalCenter.headerBackgroundImage");
可以跟參數(shù)椒袍,那肯定是個block驼唱,源碼:
- (TXSakuraImageViewBlock)image {
return (TXSakuraImageViewBlock)[super tx_sakuraImageBlockWithName:NSStringFromSelector(_cmd)];
}
別怕我們一層層來看,注意看注釋驹暑,有的簡單方法我就不展開了直接寫結(jié)果了
- (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取出對應(yīng)的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的方法也就是調(diào)用setImage:京办,參數(shù)為valueBlock返回的值,也就是[TXSakuraManager tx_imageWithPath:keyPath];的返回值
[self send1DMsgWithSEL:sel objValue:obj];
return self;
}
看完了設(shè)置的方法帆焕,那么來看看看切換皮膚的方法(有注釋的地方才是關(guān)鍵點)
[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;
// 這里是通過文件名拿到對應(yīng)的文件路徑
_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) {
// 這里僅僅把當(dāng)前選擇的皮膚名存到沙盒
[self saveCurrentSakuraInfosWithName:name type:type];
// 這里才是關(guān)鍵惭婿,通知不恭!大部分換膚框架都免不了通知
[[NSNotificationCenter defaultCenter] postNotificationName:TXSakuraSkinChangeNotification object:nil];
return YES;
}
#ifdef DEBUG
else {
NSLog(@"resources not exists!");
}
NSLog(@"%@", _configsFilePath);
#endif
return NO;
}
看到通知是不是明白了大概了,對你猜得沒錯财饥,之前創(chuàng)建的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 {
// 一維參數(shù)换吧,self.skins1D其實就是返回上面說的全局的innerSkins1D,還記得里面存的什么嗎钥星?
/**
{
"setImage:" = {
"com.tingxins.sakura.arg.image" = "PersonalCenter.headerBackgroundImage";
};
}
*/
[self updateSakuraWith1DSkins:self.skins1D];
// 二維參數(shù)沾瓦,不知道干嘛用,好像沒用到谦炒,為了拓展贯莺?
[self updateSakuraWith2DSkins:self.skins2D];
}
老鐵們應(yīng)該已經(jīng)猜到接下來該干嘛了吧,updateSakuraWith1DSkins:
這里就不展開了宁改,里面就是拿到innerSkins1D這個全局的字典缕探,根據(jù)PersonalCenter.headerBackgroundImage
這個可以取出創(chuàng)建圖片名,然后調(diào)用setImage:方法
下載还蹲?
待更新
最后感謝看完我BB這么多爹耗,希望能帶給給需要做換膚的小伙伴有一些啟發(fā),demo待我稍作修改稍后附上秽誊,有更深入的研究后會更新此文