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,對大家的工作和興趣有幫助。

2011年10月28日 星期五

View Controller (NSObject) 之間交換資料 by Segue

延續上一篇有關 Storyboard 的簡介,現在我們要來介紹在 Storyboard 中看到的新角色 Segue。
首先我們先來秀一下 Segue 的威力。在 MyFirst5 的專案中,再新增一個 View Controller 這樣總共有兩個 View Controller 然後各在其上拉一個 UILabel 做為識別,並在 Initial View Controller 的 View 上方,放置一個 UIButton 如下圖所示。
接下來我們要做一件很直覺的事情,就是按著 Ctrl 然後從 Hello Story 的 Button Click Drag 到右邊的 Second 畫面。放開之後你會看到如下的選擇畫面。選 Modal
接著會看到新的角色出現。Segue 。如下圖
 Segue 查字典是指動詞,流暢地轉換。也可以想像它是一個過場效果。OK 就這樣。直接執行。然後按下 Show Button 你將會看到 Magic 產生。從下方的畫面。
 產生如下的畫面,其過場動畫就是 Modal View Controller 呈現的效果。
產生這個 Modal View 我們沒有寫任何的程式碼,這個就是 Segue 迷人的地方。接著要更進一步來利用 Segue. 在 MyStory.storyboard 在 Second 新增一個 Text Field 和 Button 如下畫面。
接著我想要把左邊的 Label : Hello Story 這個字樣,傳到右邊的 Text Field 上面,時間點就是按下 Show 這個按鈕的時候。不過,這個時候有一個大問題,產生這兩個 View Controller 的物件是由 Storyboard 產生的而且他們的 Class 是 UIViewController,換句話說,我們沒辦法去更動 UIViewController 內容,因為我們沒有 UIViewController.m 的原始碼。但我們可以繼承它,然後把上面兩個 View Controller 的 Class 都改成我們新增的 Class。新先增兩個 Class 都繼承自 UIViewController 各命名為 HelloController 和 SecondController。首先新增 HelloController 如下的設定

不需要產生 Xib 檔,因為 Storyboard 就提供了和 Xib 檔相當的功能。其實 Stroyboard 就是很多個 Xib 檔的組合。成功產生之後會在 Navigator 看到。如下的畫面。
然後點選 Storyboard 的 Initial View Controller 的 View Controller 圖形,把它的 Class 改成 HelloController。如下圖
相同的做法產生 SecondController 如下圖。
接著也改掉 Storyboard 中 Second 的 View Controller 圖示的 Class 成 SecondController. 如下圖。
OK 材料都準備齊了。再來思考剛剛的問題。我們要從左邊 View Controller 傳資料給右邊的 View Controller。也就是由 HelloController 的實體,傳給 SecondController 的實體,傳遞的方向和 Segue 一樣,我們要在 SecondController (接受端) 新增 propery 來接 HelloController (傳送端) 傳來的資料。於是我們在 SecondController.h 新增 property dataString 而且要新增一個 IBOutlet 用來指向 Storyboard 上 SecondController 畫面上的 Text Field 實體。如下所示。
 記得各都要有 @Synthesize 在 .m 。然後上方的小圈圈中間有個黑實圈表示和 Storyboard 的圖示連在一起了。接著我們要思考一件事,在這我們用  dataString 來接受資料,要在什麼時候放到 inputField 上面呢?在 viewDidLoad。所以在 SecondController.m 的 viewDidLoad 如下表示。

現在接受端準備好了,傳送端要如何傳送呢?直覺的想就是程式碼要寫在 HelloController 裡。的哪?介紹一個新的 method。
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
這個method 會在 segue 在呼叫起 View Controller 之前被呼叫,我們來看看要寫什麼?
在 line 60,我們可以用 segue 的  destinationViewController 就可以拿到執行中 SecondController 的實體。然後把 @"From Hello" 傳給 SecondController 的 property dataString 。執行之後,按下按鈕,你會看到如下畫面。
From Hello 傳到 Text Field 上面了。Good。但好像不是我們要的。我們想把 Hello Story 這個 UILabel 傳到 Text Field 上,於是在 HelloController.h 要新增 IBOutlet UILabel * helloLabel。然後 prepareForSegue:sender: 改成如下。
在 Line 63 我們看到了 helloLabel.text 傳到 SecondController 實體的 Text Field 。執行之後就是如下。

恭喜大家。到此還沒介紹如何用到 Done 這個按鈕,還有我們接下來要反過來從 SecondController 實體傳資料給  HelloController 的實體。請期待下一篇。

2011年10月27日 星期四

About Storyboard

這篇的主題主要是來討論在 Xcode 4.2 中新的功能或是概念。有一個新的檔名,Storyboard。看名字就知道這個檔要表達的,就是一個故事,就是 App 要說的事情,換句話說,看了這個檔的內容大概就可以掌握整個 App 想要做的事。OK。
由於我個人喜歡從簡單到難,所以接下來的例子,就從一個最簡單的 Template 開始:Empty Application.
如下畫面,在開啟新專案的時候選擇 Empty Application。

就命名為 MyFirst5,記得勾選 Use Automatic Reference Counting 如下所示.
存完檔之後呢,會發現。竟然沒有 Storyboard 檔,果然是 Empty。
如果這個時候比較心急的朋友,直接 Run。當然是看到如下的畫面
好吧,我們來新增自己的 Storyboard。新增 File,然後選擇 User Interface -> Storyboard

命名為 MyStory 好啦。在專案裡就會出現這個 Storyboard 檔。
而且在右側的編輯區當然也是什麼東西都沒有。第一步就是加入一個 View Controller。從 Inspector Library 拉一個 View Controller 到 MyStory.storyboard。
首先你會看到一個白色的畫面和一個向右的箭號。
 
因為在 Storyboard 上面可以放置很多個 View Controller。要有這個箭號表示,Storyboard 第一個要執行的 View Controller。接著我們要在這個 View Controller 白色的 View 上面放上一個 Label。從 Library 拉一個 Label放到上面。如下圖所示。
所以我們預期執行的時候可以呈現上面那張圖。但是在此時執行的結果,如下圖。還是白白一張。
最目前為止?還少了什麼呢?想想。剛剛新增了 Storyboard,但是還沒有和 Xcode 說,也就是說沒有設定在這個專案的設定檔裡。專案的設定檔都是  info.plist。在這個專案就是。MyFirst5-info.plist 如下圖可以找到。
在其內容設定裡新增一個設定,Main storyboard file base name 寫上 MyStory 如下圖所示。
寫上Storyboard 名稱,再執行。發現。還是白白的一張。
 好吧,該設定的都設定了,還少了什麼嗎?其實還是少了去檢查,執行的時候會去做的事。也就是說到目前為止,都是靜態的設定,執行的時候會先去執行什麼 method 呢?答案就是 xxxAppDelegate.m 裡的application:didFinishLaunchingOptions:。看看裡面的內容。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
     self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
     self.window.backgroundColor = [UIColor whiteColor];
     [self.window makeKeyAndVisible];
return YES;
}
原在這個 Method 裡 self.window 又被改掉,當然就無法顯示出 Storyboard 加的 Label。建議就直接 Mark / Comment 如下所示:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    //self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}
再執行就可以看到如下的畫面了。