またしてもDelegate

appを組むためのテストappをいろいろ書いているのだが、なかなかうまくいかない。
TableViewにモーダルビューから取り込んだテキストを表示したいのだが、Delegateでつまづく。
そこでもう一度、きちんとDelegateをやり直してみることにした。

ケルトンプロジェクトを作成

View-based Applicationで名前は"ModalViewEX2"

子ビューの追加

"ファイル"->"新規ファイル..."を選択、"UIViewController subclass"を選び、"With XIB for user interface"にチェックを入れて、"次へ"進む。
ファイル名は"ChildViewController"として、"同時にChildViewController.hも作成"にチェックする。

Text Fieldを入れる

"ModalViewEX2ViewController.h"、"ChildViewController.h"にそれぞれUITextFieldを参照するIBOutletを作る。
"ModalViewEX2ViewController.xib"、"ChildViewController.xib"を(Interface Builderで)開き、Text Fieldを追加する。
作ったUITextFieldへの参照と、Text Fieldを接続する。
さらに、Text FieldのDelegateを"File's owner"に設定する。

TextFieldDelegateを追加する

"ModalViewEX2ViewController.h"、"ChildViewController.h"のそれぞれにプロトコルを追加する。
"ModalViewEX2ViewController.m"、"ChildViewController.m"に以下を追加


- (BOOL) textFieldShouldReturn:(UITextField *)textField
{
[ textField resignFirstResponder ];
return YES;
}
ここまででビルドし実行すると、親ウインドウのテキストフィールドに入力ができ、return/改行するとソフトウェアキーボードが閉じるはずだ。

モーダルビューを開く

"ModalViewEX2ViewController.m"の行頭の"#import..."の下に、"#import "ChildViewController.h""を追加した上で、以下のメソッドを実装


- (void) changeToChildViewController
{
ChildViewController* childVC = [ [ ChildViewController alloc ] init ];
[ self presentModalViewController: childVC animated: YES ];
childVC.textfieldChild.text = textfieldParent.text;
[ childVC release ];
}
ここで出てくる"textfieldParent"と"textfiledChild"は、先ほどInterface Builderで作ったText Fieldへの参照である。
"textfieldChild"がプロパティとして使えるように、宣言を追加する必要があるが、これは後記を参照のこと。
先ほど、"ModalViewEX2ViewController.m"に実装した"textFiledShouldReturn"に、以下のように一行追記する。

- (BOOL) textFieldShouldReturn:(UITextField *)textField
{
[ textField resignFirstResponder ];
[ self changeToChildViewController ];
return YES;
}
これでビルドし実行すると、親ビューのテキストフィールドに入力ができ、return/改行するとソフトウェアキーボードが閉じた後に、子ビューがアニメーション付きで表示されるはずだ。
しかもそのときには、親ビューのテキストフィールドに入力した内容が、子ビューのテキストフィールドにも反映されているはずである。

親ビューへのDelegateの実装

まず子ビュー側の準備をする。
"ChildViewController.h"を以下のように、変更する。


@protocol ModalViewEX2ViewControllerDelegate;

@interface ChildViewController : UIViewController {
IBOutlet UITextField* textfieldChild;
id parentDelegate;
}

- (void) setNameToParent;

@property ( assign, nonatomic ) id parentDelegate;
@property ( retain, nonatomic ) UITextField* textfieldChild;

@end

@protocol ModalViewEX2ViewControllerDelegate
- (void) setName: (NSString*) stringName;
@end

ここで新しく追加している@protocolが、親ビューへdelegateする内容である。
@interfaceで設定しているプロパティ"id parentDelegate"が、親ビューへの参照である。
2個目の@propertyが、先ほど触れた"textfiledChild"をプロパティとして他のクラスから使用できるための宣言である。
これに加えて"ChildViewController.m"にも、

@implementation ChildViewController

@synthesize parentDelegate;
@synthesize textfieldChild;

と、synthesizeを加える必要がある。


次に、子ビューが親ビューにdelegateするメソッドの追加である。
"DelegateEX2ViewController.h"を以下のように、変更する。


#import
#import "ChildViewController.h"

@interface ModalViewEX2ViewController : UIViewController {
IBOutlet UITextField* textfieldParent;
}

- (void) changeToChildViewController;
- (void) setName: (NSString*) stringName;
@end

2個目のメソッド"(void) setName: (NSString*) stringName"が子ビューが親ビューにdelegateするメソッドである。
そしてメソッドを"DelegateEX2ViewController.m"に実装する。

- (void) setName:(NSString *)stringName
{
textfieldParent.text = stringName;
}

Delegateを使用する

実装したDelegateを使用するための記述を追加する。

  • 親ビューへの参照を子ビューに引き渡す

"ModalViewEX2ViewController.m"の"changeToChildViewController"に、親ビューへの参照を子ビューに引き渡す記述を追加。


- (void) changeToChildViewController
{
ChildViewController* childVC = [ [ ChildViewController alloc ] init ];
childVC.parentDelegate = self;
[ self presentModalViewController: childVC animated: YES ];
childVC.textfieldChild.text = textfieldParent.text;
[ childVC release ];
}

  • 子ビューでdelegateされたメソッドを使用する

"ChildViewController.h"で宣言だけしてあった"setNameToParent"を実装する。
"ChildViewController.m"に以下を追加する。


- (void) setNameToParent;
{
[ parentDelegate setName: textfieldChild.text ];
[ self dismissModalViewControllerAnimated: YES ];
}
そしてこのメソッドが、ソフトウェアキーボードが閉じられた時に使用されるように、"textFieldShouldReturn"を以下のように変更する。

- (BOOL) textFieldShouldReturn:(UITextField *)textField
{
[ textField resignFirstResponder ];
[ self setNameToParent ];
return YES;
}
これで、子ビューでソフトウェアキーボードが閉じられると、子ビューが閉じ、そのときの子ビューのテキストフィールドの内容が、親ビューのテキストフィールドに反映されるはずである。