1. 程式人生 > >在故事板中載入 nib 時 IBOutlet 為 nil

在故事板中載入 nib 時 IBOutlet 為 nil

故事板是在 iOS 5 開始出現的,在此之前我們使用的是 nib/xib。一個故事板支援多個 ViewController,同時可以在這些 ViewController 中進行連線(segue)。但是隨著工程中 ViewController 的增加,故事板中 ViewController 之間的連線變得紛亂複雜,故事板也就失去了原來的意義。與之相比較,nib/xib 只支援單一的 ViewController,這種因為簡單帶來的清晰使得它在專案中仍然被大量使用。

但是,如果在專案中混合使用故事板和 nib 檔案,可能會帶來一些意想不到的問題。一個典型的問題是,storyboard 載入 nib 中的 view controller 時,這個 view controller 中的 IBOutlet 都會丟失連線,這樣在 viewDidLoad 後,所有的 IBOutlet 屬性都為 nil,你根本無法使用這些屬性。你可以來看一個例子。

  1. 新建一個 Single View 專案。

  2. 新建一個 UIViewController 子類,名字叫做 TestViewController,確認勾上 Also create xib file。

  3. 開啟 TestViewController.xib 檔案,拖入一個 label,為這個按鈕建立一個到 IBOutlet 屬性 label 的連線。

  4. 在 TestViewController.m 的 viewDidLoad 方法中,加入這句:

     _label.text = @"我是一個 Label";
    

    同時在這句上打一個斷點。

  5. 開啟 Main.storyboard。在 ViewController 上放一個按鈕。title 設定為“下一步”。

  6. 拖入一個 UIViewController。 將 Class 改成 TestViewController。

  7. 使用 Embed in Navigation Contorller,將 View Controller 外面套上一個導航控制器。

  8. 在 View Controller 和 TestViewController 之間新增一個 segue,ID 不妨叫做 test。

  9. 為 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];

效果也是一樣的。