iOS底層實現(xiàn)九宮格菜單可配置碍遍、頁面跳轉不需要#import "xxx.h"(可直接使用)

一、需求:

1阳液、每新跳一個頁面都需要導入其文件,導致頭文件很多帘皿,先要刪除這些頭文件,還能實現(xiàn)頁面跳轉
2鹰溜、項目后期做組件化,涉及到跨業(yè)務組件的話曹动,需要解耦
3斋日、在做個性化定制時恶守,可根據(jù)后臺返回,實現(xiàn)可配置(例如九宮格菜單可配置)贡必。

當然,我先給出代碼衫樊,附帶案例, 框架(WGControllerPush)可直接拖到項目中使用

項目中常會有下圖情況出現(xiàn),現(xiàn)在做到去除這些都文件科侈,依然可以做跳轉:

二、實現(xiàn)步驟:

1兑徘、頁面?zhèn)髦?/h5>

頁面跳轉首先會遇到傳值問題,大體分為四類:

a、不需要傳值
b、只通過屬性傳值

例如:

@property (nonatomic, copy) NSString *name;
c欲侮、重寫了init方法

例如:

- (instancetype)initWithDic:(NSDictionary *)dic model:(WGModel *)model school:(NSString *)school;
d、既有屬性傳值威蕉,重寫了init來傳值

例如:

@property (nonatomic, assign) BOOL isMale;

- (instancetype)initWithDic:(NSDictionary *)dic name:(NSString *) name;
2、根據(jù)上面?zhèn)髦捣诸惾驼牵紫葘懸粋€傳值類型的枚舉
#pragma mark -傳值類型
typedef NS_ENUM(NSInteger , WGPushControllerType) {
    WGPushNoParam     = 0, //不需要傳值
    WGPushProperty   = 1, //只有屬性傳值
    WGPushInit     = 2, //重寫了init方法傳值
    WGPushOther  = 3, //其他,混合方式傳值(既有屬性又有initWith)等
};
3虑粥、因為沒有導入頭文件,所以不能再如下圖這樣
4娩贷、怎么辦呢第晰?

舉個例子:

Person *per = [[Person alloc] init];
可以拆分為
Person *per = [Person alloc];
[per init];
(1)在知道一個類的字符串名字時,可以轉為Class的

就是這句:

Class classCon = NSClassFromString(toCon);
(2)有這個類的Class彬祖,是不是就可以alloc init了
PS:要用id類型接收
id con = [[classCon alloc] init];
(3)初始化完成茁瘦,然后判斷是否需要傳值:根據(jù)上面的枚舉
//根據(jù)HuPushControllerType判斷是否有值傳到下一頁
    switch (type) {
        case WGPushNoParam: {
              //不需要傳值
        } break;
        case WGPushProperty: {
            //只屬性傳值
            [self getToConFromProperty:paramDic toCon:con];
        } break;
        case WGPushInit: {
            //只通過重寫init傳值
            con = [self getToConFromInit:paramDic classCon:classCon];
        } break;
        case WGPushOther: {
            //既有屬性傳值,還重寫init傳值
            con = [self getToConFromInit:paramDic classCon:classCon];
            [self getToConFromProperty:paramDic toCon:con];
        } break;
        default:
            break;
    }
(4)重點來了储笑,要傳值咯
1甜熔、屬性傳值
我這里簡單說下思路:

1、把需要通過屬性傳值的值和屬性名字放到一個字典中突倍,值為字典的value腔稀,屬性的字符串名字為字典的key

PS:這個字典再用一個寫死的key(自己定義好一個宏就可以,我是寫了一個@"property")拼成一個大的字典, 來讓程序通過這個key拿到需要屬性傳值的字典

例如:

@property (nonatomic, copy) NSString *name;

組裝成字典為:

//存儲值的字典
NSDictionary *ParamDic = @{@"name": @"小明"};
//外層字典
NSDictionary *dic = @{@"property":ParamDic};

2赘方、根據(jù)@"property"關鍵字烧颖,獲取存值的字典,然后獲取字典的所有key數(shù)組
3窄陡、遍歷key數(shù)組炕淮。根據(jù)key的字符串生成對應屬性的set方法
4、最后通過objc_msgSend完成賦值

:代碼:
//屬性傳值
- (void)getToConFromProperty:(NSDictionary *)paramDic toCon:(id)toCon {
    //需要屬性傳值跳夭,則通過運行時來解決
    if (!toCon) {
        return;
    }
    NSDictionary *propertyDic = [paramDic valueForKey:WGProperty];
    NSArray *keyArr = [propertyDic allKeys];
    for (int i = 0; i < keyArr.count; i++) {
        NSString *key = [keyArr objectAtIndex:i];
        id value = [propertyDic valueForKey:key];
        //把key的首字母大寫
        NSString *firstStr = [key substringWithRange:NSMakeRange(0, 1)].uppercaseString;
        NSString *restStr = [key substringFromIndex:1];
        //生成對應屬性的set方法
        NSString *selName = [NSString stringWithFormat:@"set%@%@:", firstStr, restStr];
        SEL method = NSSelectorFromString(selName);
        if ([toCon respondsToSelector:method]) {
            //等價于controller.shuxing = value;
            //如果是數(shù)字則在字典中是NSNumber類型,需要把NSNumber類型轉為NSInteger或者CGFloat
            if ([value isKindOfClass:[NSNumber class]]) {
                NSString *vale = [(NSNumber *) value stringValue];
                if ([vale containsString:@"."]) {
                    CGFloat val = [vale doubleValue];
                    void (*action)(id, SEL, CGFloat) = (void (*)(id, SEL, CGFloat)) objc_msgSend;
                    action(toCon, method, val);
                }else{
                    NSInteger val = [(NSNumber *) value integerValue];
                    void (*action)(id, SEL, NSInteger) = (void (*)(id, SEL, NSInteger)) objc_msgSend;
                    action(toCon, method, val);
                }
            } else {
                void (*action)(id, SEL, id) = (void (*)(id, SEL, id)) objc_msgSend;
                action(toCon, method, value);
            }
        }
    }
}
2涂圆、重寫init方法傳值
我這里簡單說下思路:

1、根據(jù)重寫的init方法名字作為key币叹,每一個要傳的值按對應順序放到數(shù)組中在作為value润歉,放到一個字典中

PS:同上,寫死一個key(我是用的@"initWith")拼成一個大的字典

例如:

- (instancetype)initWithAge:(NSInteger)age name:(NSString *) name;

組裝稱字典:

//存儲值得字典
NSDictionary *ParamDic = @{@"initWithAge:name:":@[18, @"小明"]};
//外層字典
NSDictionary *dic = @{@"initWith":ParamDic};

2颈抚、根據(jù)@"initWith"關鍵字踩衩,獲取存值的字典,然后根據(jù)字典獲取第一個key(其實只有一個key 就是initWithAge:name:)
3、根據(jù)key獲取一個數(shù)組驱富,里面存的是參數(shù)的值
4锚赤、這時通過objc_msgSend先alloc,然后initwith···
5褐鸥、要求按順序放入數(shù)組中就是為了在init時值不會錯亂

代碼:
//init傳值,類似于initWithDic
- (id)getToConFromInit:(NSDictionary *)paramDic classCon:(Class)classCon {
    id toCon = nil;
    NSDictionary *initDic = [paramDic valueForKey:WGInitWith];
    NSString *key = [initDic allKeys].firstObject;
    //把OC的字符串改成C語言的字符串
    const char *ky = [key UTF8String];
    NSArray *value = [initDic valueForKey:key];
    //這里判斷value數(shù)組元素個數(shù)是否和 key按:分割成數(shù)組后的個數(shù)相等
    if ([key containsString:@":"] && value) {
        if ([key componentsSeparatedByString:@":"].count == (value.count + 1)) {
            switch (value.count) {
                case 1: {
                    //先alloc
                    id classAlloc = objc_msgSend(classCon, sel_registerName("alloc"));
                    //sel_registerName(ky)等價于@selecter(ky)
                    if ([classAlloc respondsToSelector:sel_registerName(ky)]) {
                        id paramOne =  [value objectAtIndex:0];
                        if ([paramOne isKindOfClass:[NSNumber class]]) {
                            NSString *val = [(NSNumber *) paramOne stringValue];
                            if ([val containsString:@"."]) {
                                CGFloat vale = [val doubleValue];
                                id (*action)(id, SEL, CGFloat) = (id(*)(id, SEL, CGFloat)) objc_msgSend;
                                //等價于[[class alloc] iniWith:]
                                toCon = action(classAlloc, sel_registerName(ky), vale);
                            }else{
                                id (*action)(id, SEL, NSInteger) = (id(*)(id, SEL, NSInteger)) objc_msgSend;
                                //等價于[[class alloc] iniWith:]
                                toCon = action(classAlloc, sel_registerName(ky), [val integerValue]);
                            }
                        }else{
                            id (*action)(id, SEL, id) = (id(*)(id, SEL, id)) objc_msgSend;
                            toCon = action(classAlloc, sel_registerName(ky), paramOne);
                        }
                    }
                } break;
                case 2: {
                    id classAlloc = objc_msgSend(classCon, sel_registerName("alloc"));
                    if ([classAlloc respondsToSelector:sel_registerName(ky)]) {
                        id paramOne = [value objectAtIndex:0];
                        id paramTwo = [value objectAtIndex:1];
                        id (*action)(id, SEL, id, id) = (id(*)(id, SEL, id, id)) objc_msgSend;
                        toCon = action(classAlloc, sel_registerName(ky), paramOne, paramTwo);
                    }
                } break;
                case 3: {
                    id classAlloc = objc_msgSend(classCon, sel_registerName("alloc"));
                    if ([classAlloc respondsToSelector:sel_registerName(ky)]) {
                        id (*action)(id, SEL, id, id, id) = (id(*)(id, SEL, id, id, id)) objc_msgSend;
                        toCon = action(classAlloc, sel_registerName(ky), [value objectAtIndex:0], [value objectAtIndex:1], [value objectAtIndex:2]);
                    }
                } break;
                case 4: {
                    id classAlloc = objc_msgSend(classCon, sel_registerName("alloc"));
                    if ([classAlloc respondsToSelector:sel_registerName(ky)]) {
                        id (*action)(id, SEL, id, id, id, id) = (id(*)(id, SEL, id, id, id, id)) objc_msgSend;
                        toCon = action(classAlloc, sel_registerName(ky), [value objectAtIndex:0], [value objectAtIndex:1], [value objectAtIndex:2], [value objectAtIndex:3]);
                    }
                } break;
                default:
                    break;
            }
        }
    }
    return toCon;
}

三:到這里已經完成了前兩個需求了线脚,還有一個九宮格菜單可配置

九宮格的demo我也寫好了,可下載參考(代碼在最上面)

1叫榕、把需要可配置的每個類的字符串名字和后臺統(tǒng)一,用一個唯一標示pageId代替浑侥,例如@"1-1"代表@"HuInformationDetailViewController"
2、客戶端本地用字典存儲所有這些pageId
例如:

NSDictionary *ParamDic = @{@"1-1":@"FirstViewController",
                            @"1-2":@"FirstViewController"
                             };

3晰绎、這時后臺只需要返給客戶端一個pageId寓落,客戶端就可以根據(jù)這個id知道點擊這個菜單按鈕時,跳轉到哪里了寒匙,這樣是不是完成了點擊功能
4、配置菜單按鈕的圖標和數(shù)量的話考蕾,本地給一個默認圖標和數(shù)量会宪,然后通過后臺返回的最新圖標和按鈕數(shù)量來更新,這樣就完成了圖標的替換塞帐,菜單個數(shù)更新巍沙。

這是我項目中本地部分默認數(shù)組:可參考

defaultSkinArr = @[
            @{
                @"code" : @"1-2",
                @"defaultImg" : @"",
                @"name" : @"必修",
            },
            @{
                @"code" : @"1-3",
                @"defaultImg" : @"educate_share",
                @"name" : @"選修",
            },
            @{
                @"code" : @"1-6",
                @"defaultImg" : @"peixun",
                @"name" : @"分組培訓",
            }
        ]
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末句携,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子削咆,更是在濱河造成了極大的恐慌蠢笋,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞻惋,死亡現(xiàn)場離奇詭異,居然都是意外死亡馁害,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來限寞,“玉大人仰坦,你說我怎么就攤上這事∶钓” “怎么了妈橄?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長鼻种。 經常有香客問我沙热,道長,這世上最難降的妖魔是什么投队? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任敷鸦,我火速辦了婚禮雁芙,結果婚禮上,老公的妹妹穿的比我還像新娘兔甘。我一直安慰自己,他們只是感情好蟆淀,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著褒链,像睡著了一般疑苔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上兵迅,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天薪贫,我揣著相機與錄音,去河邊找鬼扯夭。 笑死鞍匾,一個胖子當著我的面吹牛,可吹牛的內容都是我干的藕筋。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼隐圾,長吁一口氣:“原來是場噩夢啊……” “哼暇藏!你這毒婦竟也來了濒蒋?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤瓮顽,失蹤者是張志新(化名)和其女友劉穎围橡,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拣播,經...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年谍倦,在試婚紗的時候發(fā)現(xiàn)自己被綠了泪勒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡曹洽,死狀恐怖辽剧,靈堂內的尸體忽然破棺而出怕轿,到底是詐尸還是另有隱情辟拷,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布诀紊,位于F島的核電站隅俘,受9級特大地震影響,放射性物質發(fā)生泄漏为居。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一贰镣、第九天 我趴在偏房一處隱蔽的房頂上張望膳凝。 院中可真熱鬧,春花似錦上煤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽遥赚。三九已至,卻和暖如春凫佛,著一層夾襖步出監(jiān)牢的瞬間孕惜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工毫炉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留削罩,地道東北人。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓进陡,卻偏偏與公主長得像微服,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子以蕴,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349