en

hi, it seems you are using microsoft internet explorer. it doesn't match web standard and causes problems browsing this site. please please please use mozilla firefox or google chrome instead. thank you!

zh

哦哦!您正在使用Internet Explorer 瀏覽器,它與我們的網頁標準並不相容,可能會導致畫面顯示不正常。
請改用 Mozilla Firefox 或者 Google Chrome 才能正常瀏覽本網站,謝謝!

12.30.2011

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

 

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(我們已經建立了它的介面),接著在其中實作一個按鈕事件來解散頁面,程式碼如下。

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

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


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






19 則留言:

  1. 請問可以用程式碼去切換頁面嗎?
    而不要用按鈕和拉線的方式

    回覆刪除
  2. 為什麼多了這行[self dismissViewControllerAnimated:YES completion:^{}];

    就沒辦法回到page1??

    回覆刪除
    回覆
    1. 我不是很懂你的意思,這一行程式碼的意思就是解散目前的 ViewController

      如果你在頁面二按下按鈕,執行上述程式碼,並沒有回到頁面一,那是去哪了?

      刪除
    2. 1到2 ok
      2到1 執行後程式 就崩潰了當機
      註解掉就回到第一種沒用dismiss是ok的

      刪除
    3. 你可能在 storyboard Segue上已經自動做好連結了,如過是這樣的確會有這種情強發生,

      因為你在按下按鈕時 storyboard Segue 已經將頁面二釋放掉了,所以自然沒有 instance 去 dismiss,

      如果是這樣,那也就不需要加上那一行來釋放,不過以上述的範例來說如果使用 storyboard Segue 做釋放頁面二,會在多產生一個頁面一的 instance..造成無窮迴圈..

      刪除
    4. 所以要先把2→1的storyboard Segue刪掉 才會出現dismiss效果
      感謝 指導 我剛剛試ok了

      刪除
    5. 嗯嗯
      不用客氣!

      刪除
  3. 匿名4/06/2013

    牛奶你好:
    我看到一個程式是利用xib去寫的然後在delegate生成view,但是我想把它套用到storyboard有哪些地方是我需要改變的.

    回覆刪除
    回覆
    1. 您好:

      不用改變任何東西,storyboard 一次可以在介面上設計多個 UIViewController 也能表達他們彼此間的關係,而 Xib 僅能有單一一個,
      如果你把 xib 的概念直接套用到 storyboard 是可以行的,但是反之則會出現 association 的問題。

      刪除
    2. 匿名4/06/2013

      牛奶你好:
      那如果他在delegate檔裡面有 alloc init viewController ,我要怎麼轉換它變成在storyboard可以執行的呢。是要拉出outlet嗎?

      刪除
    3. 您好:

      完全一樣意思,只是附檔名由xib換成storyboard,你也可以把整個storyboard當做xib使用,只放置一個UIViewController,其他的都動態產生。

      刪除
    4. 匿名4/06/2013

      牛奶你好:
      真的很抱歉我是初學者,對於你的意思我還是有點不了解,程式碼如下:
      window = [[UIwindow alloc]initWithFram:[[UIScreen mainScreen] bounds]];

      TokenFieldExampleViewController * viewController = [[TokenFieldExampleViewController alloc] init];

      我程式碼到這裡就會出現"_OBJC_CLASS_$_TITokenField",referenced from:......

      希望大大可以給我解答 謝謝:(

      刪除
    5. 您好:

      大家都有當初學者的時候,我建議您可以先用 single view application 的樣板開啟專案,比較一下兩者不同,你就會知道了。

      你可以參考下面的網站,我覺得他的問題跟您蠻像的。

      http://stackoverflow.com/questions/8705815/iphone-ios5-storyboard-how-to-load-a-uiviewcontroller-with-a-custom-xib-file

      加油!

      刪除
  4. 匿名4/07/2013

    牛奶你好:
    謝謝你,這真的是一個很棒的網站,那如果他沒有用storyboard也沒有使用xib而是在delegate裡面生成view我一樣可以用這個方法嗎?

    回覆刪除
    回覆
    1. 您好:

      一樣可以,如果你想要連動最一開的 ViewController 都是動態產生,你可以在 delegate.m 下設定中斷點,了解一下如何運作。

      - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 函式會在應用程式ㄓ 準備完成時被觸發,你也可以在此函式中做介面設計,或是產生新的ViewController。

      下面這個頁面是使用空白專案開始,到 Single View Application 的製作過程,你可以在一開始的地方看到,上述的函式是如何格式化現有的視窗畫面(白色底)。
      http://furnacedigital.blogspot.tw/2012/01/empty-application-single-view.html

      刪除
  5. 匿名8/27/2013

    牛奶你好~
    請問使用UIAlertView這個元件,在彈出視窗的按鈕能有方法能做換頁的功能嗎?

    回覆刪除
    回覆
    1. 您好:
      你這樣問我一定都會說可以的阿,只是這樣就不需要透過 Storyboard Segue 來換頁面,因為你的UIAlertView一定是動態產生的。

      必須注意幾點:
      -將UIAlertView中的按鈕做好事件的連結,在按下按鈕時所要觸發的函式。
      -被觸發的函式中建立新的 ViewController 來做切換,這邊你可以會需要使用到delegate的技巧來傳遞參數。



      刪除
  6. 牛奶你好:
    想請問一下我有一個主View加4個ViewController,這5個View裡面皆有個滑動菜單,菜單裡面有四個按鈕,按下後分別會切換到ViewControllerA,B,C,D
    我是使用[self presentViewController:ViewControllerA animated:NO completion:^{}];來切換
    第一次(從主View連到其他ViewController)可以正常運作
    但第一次過後(從非主View連到其他View)卻會崩潰

    請問一下是我哪邊做錯了嗎

    回覆刪除
    回覆
    1. 您好:

      我覺得不需要這麼多的 ViewController ,如果你的配置上都一樣,可以考慮使用一個就好,其他的則動態產生或是直接替換主要的內容。

      另外崩潰的原因推測與找不到instance有關,你可以試著將 dismissViewControllerAnimated 加入看看,因為這樣可以確保你返回正確的ViewController,例如 :主畫面 -->ViewController A-->ViewController C
      在實作上可以寫成
      主畫面 --(按下前往頁面A)-->presentViewController: ViewController A
      --(按下前往頁面C)--> dismissViewControllerAnimated (先回到主畫面) ---->presentViewController:ViewController C

      刪除