使用 Storyboard Segue 實作 UIViewController 的切換

来源:互联网 发布:虹港数据 idc机房 编辑:程序博客网 时间:2024/05/23 13:02

原帖地址 :http://furnacedigital.blogspot.com/2011/12/storyboard-segue-uiviewcontroller_30.html


 

Storyboard 是在 iOS 5 SDK 中才出現的新名詞,它其實就是原本的 Xib 檔案(Interface Builder),用來製作介面排版方面的工具,當然在 Storyboard 裡也多了很多應用的元件,其中  Storyboard Segue 可以讓你幾乎連程式碼都不用寫,就輕鬆完成兩個 UIViewController 的切換工作,以下是我們的示範。

首先在開啓新專案時選擇 Single View Application 來簡化流程,可以少製作一個 UIViewController 與它對應的 class 檔,如果各位有需要當然也可以開啓完全空白的乾淨的新專案,再自行加入 storyboard 與 UIViewController。

接著來到專案下的 storyboard 畫面,從右下方的元件庫中拉一個 UIViewController 到 storyboard 中,並且分別對兩個 UIViewController 做介面上的設計:增加 Navigation Bar 來辨識彼此,與一個用來做頁面切換按鈕,如下圖。


我們希望在頁面 1 按下前往頁面 2 的按鈕時,畫面能切換至頁面 2,之後也能以同樣的方法返回頁面 1,為了達到目的,必須使用 Storyboard Segue 元件將兩個 UIViewController,給連起來,由於 Storyboard Segue 是動態產生的,所以並不會出現在元件 Storyboard 的元件庫中。

產生 Storyboard Segue 的方法常簡單,就如同替介面元件和程式碼做連結的拖曳方式一樣的直覺,使用滑鼠右鍵將頁面 1 內的按鈕拖曳連結至頁面 2 ,並選擇 Model 做連結,Storyboard Segue 就會自動連結兩個 UIViewController,如下圖。


現在你可以用模擬器執行看看,在 Segue DEMO 頁面 1 按下前往頁面 2 的按鈕時,畫面就會切換至頁面 2,如果想要使用不同的換頁效果,可以在 Storyboard Segue 中的 Transition 屬性做修改,範例中所使用的是翻頁 Partial Curl 效果。

下來就是從頁面 2 「返回」頁面一,在這裡我們特別強調「返回」而不是前往,一個錯誤的做法,是按照上面的步驟將頁面 2 的按鈕拖曳連結至頁面一的 Model,使整個 Storyboard 的佈局如下圖。


如果你按照這樣的設計方式來執行,你會發現兩個頁面的確可以互相切換,功能上是正確,但是背後的意義卻大不相同,而且在多次切換後很有可能創造過多的 UIViewController 實體,這樣做畫面每次都會朝同樣的方向來做切換的效果,每次都會建立一個新的 UIViewController 實體來使用。

我想大家應該都猜到正確的做法應該是在頁面 2 的程式碼中使用 dismissModalViewControllerAnimated: 方法,或是 dismissViewControllerAnimated:completion: 方法來解散頁面 2,並返回先前的頁面。

替專案新增一個 UIViewController 的 Subclass,我們命名為 Page2ViewController,並取消 With XIB for user interface(我們已經建立了它的介面),接著在其中實作一個按鈕事件來解散頁面,程式碼如下。

1
2
3
- (IBAction)returnToFirstPage:(id)sender {
  [self dismissViewControllerAnimated:YES completion:^{}];
}

最後回到專案下的 Storyboard,將我們新產生的 UIViewController 與 Page2ViewController 做連結,連結的方式是在 UIViewController 的屬性中選擇對應的 class 檔,如下圖,之後將其按鈕元件與 Page2ViewController 的按鈕事件做連結即可。


現在再用模擬器執行看看,按下頁面 2 中的按鈕,畫面會以相反的方向的切換效果來返回頁面 1。

使用 Storyboard Segue 實作 UIViewController 的切換(下)

 

延續前一篇的文章使用 Storyboard Segue 實作 UIViewController 的切換(上),我們已經成功建立兩個可以互相切換的 UIViewController,一個是透過 Storyboard Segue 來切換,另一個則是使用 dismissViewControllerAnimated: 的方法來返回先前的 UIViewController,接下來就是要解決兩個 UIViewController 之間傳值的問題,這裡同樣提供兩個方法,在頁面 1 的部份同樣使用 Storyboard Segue 來幫助我們傳遞資訊給頁面 2,而在頁面 2 的部份,則是使用老方法,透過代理委托 delegate 的方式來傳送資訊。

首先我們在兩個 UIViewController 分別拉一個 UITextField 元件,並將此元件與 class 做連結,分別命名為 page1TextField 與 page2TextField,連結的方式可以參考從使用 UIButton 說 Hello 開始說起(上)ㄧ文,如果是使用 Xcode 4 的朋友也可以直接參考 Xcode 4 的 Assistant Editor 關聯編輯功能ㄧ文,節省撰寫程式碼的時間。

在透過 Storyboard Segue 傳值的部份,我們必須在頁面 2 的 UIViewController class 裡設置一個 NSString 的變數,它的目的是用來接收由頁面 1 透過 Storyboard Segue 所傳過來的資訊,程式碼如下。

1
2
3
@property (weak) NSString *string;
 
//別忘了在對應的實作檔中加入@synthesize string;

之後在 viewDidLoad 的函式裡我們將 string 的值指定給 page2TextField,這個動作會讓頁面 2 的畫面在被開啓時就會把 page2TextField 的內容設成 Storyboard Segue 所傳送過來的值。

1
2
3
4
5
6
- (void)viewDidLoad
{
    [super viewDidLoad];
 
    page2TextField.text = string;
}

接著回到頁面 1 的 UIViewController class 裡,新增一個內建的函式如下,就完成透過 Storyboard Segue 傳值的方法。

1
2
3
4
5
6
7
8
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
 
    //將page2設定成Storyboard Segue的目標UIViewController
    id page2 = segue.destinationViewController;
 
    //將值透過Storyboard Segue帶給頁面2的string變數
    [page2 setValue:page1TextField.text forKey:@"string"];
}

另一個方法就是使用代理委托 delegate 的方法,透過建立一個協定 @protocol 的方式,讓其它的採納此協定的 class 可以實作協定內的函式,我們在頁面 2 的 UIViewController class 裡設置一個協定,並且在頁面 1 的 UIViewController class 裡實作協定裡的方法,讓程式執行到頁面 2 時,仍然能夠取得頁面 1 的實例 Instance,進而使用協定裡的方法來設定 page2TextField 的數值。

首先來到頁面 2 的 UIViewController class,建立一個協定 Page2Delegate,並且定義其內部的方法 passValue:,接著宣告一個採用此協定的物件 delegate,其程式碼如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#import <UIKit/UIKit.h>
//建立一個協定
@protocol Page2Delegate
 
//協定中的方法
- (void)passValue:(NSString *)value;
 
@end
 
@interface Page2ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UITextField *page2TextField;
@property (weak) NSString *string;
 
//宣告一個採用Page2Delegate協定的物件
@property (weak) id<Page2Delegate> delegate;
@end

接下來就是決定協定中的方法要在什麼時候生效,也就是要在什麼地方呼叫 passValue: 方法函式,而此函式會在採用此協定的類別 Class 中被實作。回顧程式的需求,我們希望在頁面 2 按下返回頁面 1 的按鈕時,能將 page2TextField 的數值傳給頁面 1 的 page1TextField,所以呼叫協定 passValue: 方法的程式碼,勢必要寫在此按鈕事件中。

1
2
3
4
5
6
7
- (IBAction)returnToFirstPage:(id)sender {
 
    [self dismissViewControllerAnimated:YES completion:^{}];
 
    //呼叫協定中的方法並帶入page2textField的數值
    [delegate passValue:page2TextField.text];
}

現在,當我們按下頁面 2 的按鈕時,就會呼叫採用 Page2Delegate 協定的 class,而此 class 必須要實作此協定內的方法,所以回到頁面 1 的 UIViewController class,我們要替此 class 採用 Page2Delegate 的協定,並且實作協定內的 passValue: 方法函式。

採用協定的方式是在 @interface 區段的地方加上 <協定名稱> 的程式碼,由於此協定是寫在別的 class 中,所以在採用協定之前別忘了先引用它,以下是頁面 1 的 UIViewController class .h 標頭檔。

1
2
3
4
5
6
7
8
9
#import <Uikit/Uikit.h>
 
//引用持有Page2Delegate協定的class
#import "Page2ViewController.h"
 
@interface MLViewController : UIViewController <page2delegate> //採用協定
@property (weak, nonatomic) IBOutlet UITextField *page1TextField;
 
@end

接著在 .m 實作檔中實作協定內的 passValue: 方法函式。

1
2
3
4
5
- (void)passValue:(NSString *)value {
 
    //設定page1TextField為所取的的數值
    page1TextField.text = value;
}

最後一個步驟,也就是大家常常會忘記的,要將代理 delegate 設成自己(頁面 1 的 UIViewController),也就是大家在使用協定時最常寫的一行程式碼 object. delegate = self(object 指的就是頁面 2 的 UIViewController),至於這行程式碼要寫在哪裡?還記得之前透過 Storyboard Segue 傳值的部份,我們已經藉由內建的函式取得頁面 2 的 UIViewController 實例 page2,所以我們修改此內建函式,利用 page2 來設定 delegate 變數。

1
2
3
4
5
6
7
8
9
10
11
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
 
    //將page2設定成Storyboard Segue的目標UIViewController
    id page2 = segue.destinationViewController;
 
    //將值透過Storyboard Segue帶給頁面2的string變數
    [page2 setValue:page1TextField.text forKey:@"string"];
 
    //將delegate設成自己(指定自己為代理)
    [page2 setValue:self forKey:@"delegate"];
}

如果忽略此步驟,頁面 2 裡的 delegate 參數在呼叫 passValue 方法時,並不會知道是誰(哪個 class)實作了它的方法,因此參數也無法由頁面 2 傳遞至頁面 1。

原创粉丝点击