前回の記事では、GUIコントロールを配置するところまで作成したが、当然のことながら、そのままではボタンを何回押そうが何も起きないので、GUIコントロールとアプリケーションの処理を関連づける必要がある。Cocoaの正解ではOutletというやつと、Action-Targetというやつで実現するようだ。
Outletを作成する
どうもGUIコントロールの操作をアプリケーションに伝えるにはOutletというものを作って、GUIコントロールと接続する必要があるらしい。そもそもOutketとはなんぞやと思いAppleのドキュメントを見てみると、
An outlet is a property of an object that references another object. The reference is archived through Interface Builder. The connections between the containing object and its outlets are reestablished every time the containing object is unarchived from its nib file. The containing object holds an outlet declared as a property with the type qualifier of
IBOutlet
and aweak
option.
とゴチャゴチャと書いてあるが、要は「Application側でインターフェースビルダーのオブジェクトの参照を用意して関連付けするんだよ」ということらしい。具体的には、weakオプション付きのIBObject型のプロパティを用意すればいいようだ。前回あげたガイドにもAppDelegate.hにweakオプション付きのIBObject型のプロパティを追加しろと書いてあるので、言われたとおりに追加した。
#import <Cocoa/Cocoa.h> @interface AppDelegate : NSObject @property (assign) IBOutlet NSWindow *window; @property (weak) IBOutlet NSButton *button; @property (weak) IBOutlet NSTextField *textfield; @end
ヘッダーファイルだけではなく、ソースファイル側にも追記しなければならないようだ。
#import "AppDelegate.h" @implementation AppDelegate @synthesize window = _window; @synthesize button = _button; @synthesize textfield = _textfield; - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // Insert code here to initialize your application } @end
ここで登場している@propertyと@synthesizeって何者かと思い、Google先生に問い合わせてみたところ、「[iPhoneプログラミング]Objective-Cの @property と @synthesize の組み合わせが何をやっているのかを解説」という記事があった。アクセッサを自動で作っているってことらしい。というとこは、自分でアクセッサを定義すれば、特に@propertyや@synthesizeを使わなくても動くんじゃないかと思うが、変数一つ一つにアクセッサを自分で定義するのは面倒すぎるので、試しはしない。それにしても、この言語機能は便利だと思う。
OutletとGUIコントロールを接続する
Outletを作っただけでは、それがどのGUIコントロールと関連するのかは決まっていないので、GUIコントロールと関連づける必要があるようだ。
Interface Builderで青い四角の[AppDelegate]を選択すると、追加したOutletが表示されている。その右側の○から関連づけたいGUIコントロールにDrag&Dropすることで接続される。
Actionを作成する
ここまで色々追加してきたが、まだボタンを押したときにする動作自体には全く手を付けていない。戻り値がIBAction型のメソッドを作ればいいようだ。
- AppDelegate.h
#import <Cocoa/Cocoa.h> @interface AppDelegate : NSObject { int count; } @property (assign) IBOutlet NSWindow *window; @property (weak) IBOutlet NSButton *button; @property (weak) IBOutlet NSTextField *textfield; -(IBAction)pushButton:(id)sender; @end
- AppDelegate.m
#import "AppDelegate.h" @implementation AppDelegate int count = 0; @synthesize window = _window; @synthesize button = _button; @synthesize textfield = _textfield; - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // Insert code here to initialize your application } - (IBAction)pushButton:(id)sender { self->count++; NSString *buttonText = [self.button title]; buttonText = [buttonText stringByAppendingFormat:@" %dtimes",count]; [self.textfield setTitleWithMnemonic:buttonText]; } @end
(IBAction)pushButton:(ID)senderというメソッドを作成した。プッシュボタンが押されるたびにカウントアップし、テキストフィールドにボタンのタイトルと押された回数を表示するようにしている。カウント用にcount変数も追加し、インスタンス生成時に0で初期化している。Objective-Cではインスタンスのメソッドを呼び出すときは
[インスタンス メソッド名:引数]
とすればいいようだ。ちなみに@"文字列"でNSString型の文字列インスタンスを作れるらしい。
アクションとGUIコントロールを接続する
ActionもOutletと同様に、コードを書いただけではGUIコントロールと関連づかないので、Interface Builderで接続しないといけないらしい。
Outletのときと同様に、[AppDelegate]を選択し、[Recieve Action]-[pushButton]からGUIコントロールにDrag&Dropするようだ。
動かしてみる
これで、ボタンを押すとテキストフィールドに「Push Button <押した回数>times」と表示されるはず。Runボタンを押してビルドし起動してみる。<
確かに表示された。うまくいったみたいだ。
Xcodeを使うと、GUIコントロールとコードの接続がGUIで直感的に操作でき、思った以上に簡単だった。ただObjective-Cの文法がなかなか慣れない。Objective-Cから入れば違和感がないのだろうけど、C++やJavaを触っていると戸惑うのじゃないかと思う。結局慣れの問題なんだろうけど。