UIViewController解耦尝试

当咱们使用UIViewController时,从一个ViewController跳到另一个ViewController,最简单的代码(不用storyboard的状况下)就是alloc一个实例,而后用navigation controller去push它。好比git

#import "ViewController2.h"
//.....
- (void)buttonPushOnTouch:(id)sender {
    ViewController2 *vc2 = [[ViewController2 alloc]init]];
    [self.navigationViewController pushViewController:vc2 animate:YES];
}

而这样一来,这个ViewController必须知道另一个ViewController的存在,并且须要import它的头文件。这样一来,两个ViewController就紧密耦合了。github

在通常的程序里面,这么作没有任何问题。可是咱们公司的两个主打产品有一个特殊需求就是:两个产品共用部分界面,而这些共用界面中的一些按钮,点击以后,在不一样的程序里须要push出不一样的界面(界面初始化须要的参数是同样的,但就是彻底不一样的界面)。而这两个产品又是由不一样的小组去完成的,他们不共享同一个工程。json

这时,咱们想到的是将这些共用界面提取出来作成framework。那么问题就出现了:我若是在framework里面的view controller新建一个下一级view controller,就须要知道下一级view controller的头文件,下一级view controller又实际存在于每个主工程里面。这样就出现了循环依赖的状况,这样作出来的framework即便能编译经过也无法维护。app

好在ObjC是一个动态语言,实际上咱们根本不须要知道一个类的信息就能初始化它。使用的方法就是NSClassFromString()函数,此时你只要知道class名字就行,若是它返回一个Class不为nil,就能够实例化它,而且用KVC注入须要的property。因此稍加改进的代码以下:函数

NSString *viewControllerClassName = IN_PROJ_1?@"AAA":@"BBB";
Class aViewControllerClass = NSClassFromString(viewControllerClassName);
if(aViewControllerClass != nil){
    id anInstance = [[aViewControllerClass alloc]init];
    [anInstance setValue:xxx forKeyPath:@"xxx"];
    if (anInstance != nil){
        [self.navigationController pushViewController:anInstance animate:YES];
    }
}

虽然这个能解耦,可是每次写那么冗余的代码总归不易维护。因此我尝试把这些东西抽象出来,以配置文件的形式,在不一样工程里配置不一样的界面。灵感来自于前几年在写Java的时候用的Spring。最后作了一个叫作EMViewControllerManager的东西。ui

EMControllerManager的配置文件的基本结构以下:code

{
    "Test1":{
        "ClassName":"Test1ViewController",
        "Description":"The homepage of the app",
        "Tag":"100",
        "Dependencies":{
            "dependentString":"@Yes, you can inject a string using the config file",
            "dependentInt":1000,
            "dependentBool":true,
            "test2ViewController":"Test2"
        }
    },
    "Test2":"Test2ViewController"
}

其中Test1Test2被称为view controller名称(昵称)。ClassName指定了view controller的真实类名,而Dependencies能够作一点基础的DI功能。对象

而后在AppDelegate里面载入配置文件:ip

EMControllerManager *cm = [EMControllerManager sharedInstance];
NSString *path = [[NSBundle mainBundle]pathForResource:@"ViewControllerConfig" ofType:@"json"];
NSError *e = nil;
[cm loadConfigFileOfPath:path fileType:EMControllerManagerConfigFileTypeJSON error:&e];
if (e) {
    NSLog(@"%@",[e localizedDescription]);
}

而在view controller里面,能够这么来初始化一个view controller对象:ci

UIViewController *vc = [cm createViewControllerInstanceNamed:@"Test1" withPropertyValues:@{@"color":[UIColor redColor],@"number":@(1)}];
if(vc != nil){
    [self.navigationController pushViewController:vc animate:YES]
}

其中Named:是view controller的昵称,withPropertyValues:是初始化以后立刻注入的property。

不一样的工程只要改配置文件,而不须要改具体的代码,就能达到差别化的效果。

具体用法请阅读EMControllerManager的Github主页

相关文章
相关标签/搜索