2011年10月29日 星期六

Storyboard 第三部曲:Segue & Delegate

我們再來對 MyFirst5 這個 Project 新增新的功能。還記得之前的文章嗎?
About Storyboard
View Controller (NSObject) 之間交換資料 by Segue
最後我們停留的 Storyboard 畫面如下。
右邊的 Done Button 還沒有功效,除此之外再加上一個新的功能,當使用者在 Second 輸入文字後,按下 Done 就會把 Text Field 的文字傳到左邊的 View Controller 的 Label 上,也就是改變了目前看到 Hello Story 的顯示。
不過首先要加上一個很自然的動作,就是按下 Done 按鈕之後會離開 Second 畫面回到 Hello Story 這個畫面,因為Second 這個畫面是按下 Show 之後利用 Segue而呈現出來的,有呈現就有退回,我們就寫在 Done 這個按下的動作裡。
所以我們要加上一個 IBAction 來相對應 Done 按下這個事件,也就是要在 SecondController.m 新加一個 IBAction。名為 doneEditing。如下所示。
在這裡用到新的 method 在 Line 49 名為 dismissViewControllerAnimated:completion: 這個是用來取代之前的 dismissModalViewControllerAnimated: 而且 Apple 的文章也建議之後都用這個不要用 dismissModalViewControllerAnimated: 相對的,呈現的時候也有一個名為
presentViewController:animated:completion: 用來取代之前的 presentModalViewController:animated: 請大家注意。好。執行之。就可以看到按下 Show 和 Done 是個相對應的動作,在 Hello Story 和 Second 畫面之間切換。如下所示。


接著我們要從 Text Field 傳資料到 Hello Story。不過在此時,我們想一想,資料傳遞方向和 Segue 相反,所以在資料的傳送端建立 Protocol 而 資料的接受端就是 delegate 的對像,就要實作傳送端的 Protocol 提到的 method 才可以收到資料
好,我們在 SecondController 建立為了把 Text Field 的資料傳送出去的 Protocol 如下表示。在 SecondController.h。
這個Protocol名為 SecondDelegate 寫在  Line 10。再準備一個 delegate property,這個變數所指的對像就是要來收到 Text Field 上面資料的。型別就是 id<SecondDelegate> 意思就是任意的 Class 但要實作 SecondDelegate 所宣告的 method。那 SecondDelegate 這個 Protocol 的主體就在 Line 17 ~ Line 21 。唯一的一個 method 名為 second:inputString: 也就是說,要收到 Text Field 的資料的物件就要實作這個 method。那在什麼時候傳過去 delegate 的物件呢?就在 Done 按下去的時候,所以要在 doneEditing 這個 method 要再新增傳資料給  delegate 所指的物件。於是如下更改。

從 line 49 到 line 52 是新增部分,就是確定 self.delegate 有實作 second:inputString: 然後就直接呼叫了。Good,到目前為止就是傳送端準備好了,定義好 Protocol 準備好 Delegate 來使用,接下來看接收端,左邊的 HelloController 的實體,也就是 HelloController.m 要實作 second:inputString: 但我們首先在 HelloController.h 提示一下 Compiler 要實作 SecondDelegate 這個 Protocol 如下所示。
比較要注意的是在 Line 10 要 import SecondController.h 因為 Protocol 在這邊宣告,然後在 Line 11 多了 <SecondDelegate> 這個就是和 Compiler 說 .m 要實作其 method 哦。這樣 Compiler 才不會給警告。ok 在 HelloController.m 要加上 second:inputString: 的實作。如下。
很簡單就是收到 inputString 之後去更改  helloLaebel 的 text 。到此,無論是傳送端或是接收端,行為都在 Class 定義好了,執行的時候物件就會依這些定義行為。但是還有一件事情還沒做,就是 HelloController 的物件,和 SecondController 的物件還沒有關係?怎麼說?剛剛做的事情就是定義 method,忽略了,SecondController.h 不是有新增一個 delgate 變數嗎?什麼時候給值,這個變數要指定到正確的物件,值才會傳過去,那要寫在那?指定給誰?指定給 HelloController 的物件,這個比較明確,問題比較大的是寫在那?
這個問題有了 Segue 會比較明確,還記得之前一篇,從 HelloController 實體傳資料給 SecondController 實體的程式碼寫在那呢?寫在 HelloController.m 的 -(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {} 嘛,所以要把這兩個物件實體的關係綁在一起的程式碼就寫在這,因為在這可以同時存取到兩個物件實體。如下所示。
就只多了 Line 64 把 self 也就是 HelloController 的實體指定給 secondCon 的 delegate property 了。直接執行就會看到如下。
好了。三部曲告一段落,希望大家多懂一點 Storyboard,對大家的工作和興趣有幫助。

7 則留言:

  1. 作者已經移除這則留言。

    回覆刪除
  2. 您好!
    首先,要感謝您花時間編寫這些教學文件。

    我按照您的步驟進行,大致順利,
    但在 HelloController.m 的第 64 行
    [secondCon setValue:self forKey:@"delegate"];
    這一行加上去就會當掉
    GDB 上面的訊息是
    [ setValue:forUndefinedKey:]: this class is not key value coding- compliant for the key delegate.'
    我自己 google 了半天找不到答案
    想請教您,我是不是漏了什麼,是不是能給我一個方向。

    感謝!!

    回覆刪除
  3. Hi, pbx625. 請注意在 SecondController.h 要有 @property(weak) id delegate;
    在 SecondController.m 要有 @synthesize delegate;
    這要才是完整的 property 定義,才會自動產生 getter/setter。

    回覆刪除
  4. Hi 麥克,
    真糗,看起來是我打錯字了。

    謝謝您!

    回覆刪除
  5. 作者已經移除這則留言。

    回覆刪除
  6. >< 版大!!我想請問,當我在第一個viewController.h 加上
    < secondDelegate > 時...
    出現了以下錯誤,我搜尋好久,依然找不到答案。
    Cannot find protocol declaration for 'secondDelegate'。
    為什麼會找不到 secondDelegate ?
    是因為Xcode更新了嗎QQ“ 懇求解答。

    回覆刪除
    回覆
    1. Hi
      在 @interface 之上要出現 #import "SecondController.h"

      刪除