故事板是在 iOS 5 开始出现的,在此之前我们使用的是 nib/xib。一个故事板支持多个 ViewController,同时可以在这些 ViewController 中进行连接(segue)。但是随着工程中 ViewController 的增加,故事板中 ViewController 之间的连线变得纷乱复杂,故事板也就失去了原来的意义。与之相比较,nib/xib 只支持单一的 ViewController,这种因为简单带来的清晰使得它在项目中仍然被大量使用。
但是,如果在项目中混合使用故事板和 nib 文件,可能会带来一些意想不到的问题。一个典型的问题是,storyboard 加载 nib 中的 view controller 时,这个 view controller 中的 IBOutlet 都会丢失连接,这样在 viewDidLoad 后,所有的 IBOutlet 属性都为 nil,你根本无法使用这些属性。你可以来看一个例子。
-
新建一个 Single View 项目。
-
新建一个 UIViewController 子类,名字叫做 TestViewController,确认勾上 Also create xib file。
-
打开 TestViewController.xib 文件,拖入一个 label,为这个按钮建立一个到 IBOutlet 属性 label 的连接。
-
在 TestViewController.m 的 viewDidLoad 方法中,加入这句:
_label.text = @"我是一个 Label";
同时在这句上打一个断点。
-
打开 Main.storyboard。在 ViewController 上放一个按钮。title 设置为“下一步”。
-
拖入一个 UIViewController。 将 Class 改成 TestViewController。
-
使用 Embed in Navigation Contorller,将 View Controller 外面套上一个导航控制器。
-
在 View Controller 和 TestViewController 之间添加一个 segue,ID 不妨叫做 test。
-
为 View Controller 上的“下一步”按钮创建一个 IBAction buttonClicked,在里面编写代码:
-(void)buttonClicked{ [self performSegueWithIdentifier:@"test" sender:nil];
这样,当按钮一点击,导航控制器会跳到 TestViewController。
Build & run,点击“下一步”按钮,断点停下,将鼠标放在 _label 变量上查看值,发现值为 nil,继续执行,Label 上的文字不会改变。
这个问题说明,故事板在加载 nib 文件时,不会自动加载 nib 文件中的内容。为了搞清楚故事板是如何加载 TestViewController 类的,我们在 TestViewController.m 中增加两个初始化方法,并在每个方法中打上断点:
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithCoder:aDecoder];
return self;
}
-(instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil{
return [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
}
Build & run,我们发现断点在 initWithCoder: 方法中停下。说明故事板是调用这个方法来初始化 TestViewController 的。
我们将这个方法修改为:
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
self = [super initWithNibName:NSStringFromClass([self class]) bundle:nil];
return self;
}
这样,故事板就会从 nib 文件中加载 TestViewController 了。Build & run,当断点停下,观察 _label 变量,现在它不再为 nil 了:
继续执行,TestViewController 上 Label 的文字成功被我们改变成“我是一个 Label”。
当然,我们也可以用代码创建 TestViewController, 而不是让故事板为我们创建 TestViewController。
在 Main.storyboard 中删除 Test View Controller,然后修改 View Controller 的 buttonClicked 方法:
TestViewController* vc = [TestViewController new];
[self.navigationController pushViewController:vc animated:YES];
效果也是一样的。