換膚框架SakuraKit實踐及源碼解析

前言

最近在做換膚煤篙,但是找了很多方案發(fā)現(xiàn)對代碼的侵入性都非常大萌壳,而且公司項目做了組件化场梆,網(wǎng)上通用的方案雖然也能實現(xiàn)但是代價太大盔几,而且不方便迭代更新晴弃。當(dāng)換膚功能完成大半的時候突發(fā)發(fā)現(xiàn)一個流弊的換膚框架,SakuraKit我逊拍。上鞠。。
先附上原文作者的鏈接:http://www.reibang.com/p/8930b4496023
作者的 demo本人感覺過于復(fù)雜芯丧,我這里講下從簡單的使用到源碼

使用步驟

  1. 編寫json文件Skin_Tennis.json
{
   "PersonalCenter":{
        "skinFlagColor":"ffffff",
        "skinFlagName":"網(wǎng)球主題",
        "backImage":"back_black",
        "backTitleColor":"#999999",
        "headerBackgroundImage":"me_header_tennis",
        "buttonBackgroundImage":"button_background_tennis"
    }
}
  1. 調(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);
  1. 切換皮膚
[TXSakuraManager shiftSakuraWithName:@"Skin_Tennis" type:TXSakuraTypeMainBundle];
  1. 恭喜你完成了一個簡單的本地?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待我稍作修改稍后附上秽誊,有更深入的研究后會更新此文

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鲸沮,一起剝皮案震驚了整個濱河市琳骡,隨后出現(xiàn)的幾起案子锅论,更是在濱河造成了極大的恐慌,老刑警劉巖楣号,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件最易,死亡現(xiàn)場離奇詭異,居然都是意外死亡炫狱,警方通過查閱死者的電腦和手機(jī)藻懒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來视译,“玉大人嬉荆,你說我怎么就攤上這事】岷” “怎么了鄙早?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長椅亚。 經(jīng)常有香客問我限番,道長,這世上最難降的妖魔是什么呀舔? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任弥虐,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘霜瘪。我一直安慰自己珠插,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布粥庄。 她就那樣靜靜地躺著丧失,像睡著了一般。 火紅的嫁衣襯著肌膚如雪惜互。 梳的紋絲不亂的頭發(fā)上布讹,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天,我揣著相機(jī)與錄音训堆,去河邊找鬼描验。 笑死,一個胖子當(dāng)著我的面吹牛坑鱼,可吹牛的內(nèi)容都是我干的膘流。 我是一名探鬼主播,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼鲁沥,長吁一口氣:“原來是場噩夢啊……” “哼呼股!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起画恰,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤彭谁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后允扇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缠局,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年考润,在試婚紗的時候發(fā)現(xiàn)自己被綠了狭园。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡糊治,死狀恐怖唱矛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情井辜,我是刑警寧澤绎谦,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站抑胎,受9級特大地震影響燥滑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜阿逃,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一铭拧、第九天 我趴在偏房一處隱蔽的房頂上張望赃蛛。 院中可真熱鬧,春花似錦搀菩、人聲如沸呕臂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽歧蒋。三九已至,卻和暖如春州既,著一層夾襖步出監(jiān)牢的瞬間谜洽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工吴叶, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留阐虚,地道東北人。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓蚌卤,卻偏偏與公主長得像实束,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子逊彭,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫咸灿、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,066評論 4 62
  • 前言 手把手講解系列文章侮叮,是我寫給各位看官避矢,也是寫給我自己的。文章可能過分詳細(xì)签赃,但是這是為了幫助到盡量多的人谷异,畢竟...
    波瀾步驚閱讀 13,147評論 32 106
  • 2018年8月2日 星期四 晴 豆類的營養(yǎng)價值不必我多說分尸,想必大家都懂锦聊。什么降糖降脂、促進(jìn)消化箩绍、調(diào)節(jié)膽...
    山青青閱讀 4,005評論 72 102
  • 1. 午飯后剛想休息一會材蛛,忽然收到一個男同學(xué)發(fā)來的微信:你看見某某某今早發(fā)的朋友圈嗎圆到?你作為班長,能不能去提醒她一...
    徐鎂鑫閱讀 820評論 4 14
  • 1.null和undefined都被用來表示空值卑吭,當(dāng)使用不嚴(yán)格等于號(==)做判斷時芽淡,他們是等價的。 這也是為什么...
    蘇星河閱讀 799評論 0 7