17665 visitors

Cocoa GUI App

No 0  改訂履歴
No 1  はじめに
No 2  プロジェクト作成
No 3  モデル
No 4  ビュー 1
No 5  ビュー 2
No 6  ビュー 3
No 7  コントローラー
No 8  接続とビルド
No 9  一覧表示の追加
No 10  各種設定
No 11  アイコンの設定
No 12  日本語化


このサイトについて
contact me
home

Programming
C 言語
learn C
Objective-C 2.0 言語
learn ObjC
Objective-C 2.0 言語 簡易版
learn ObjC  Lite
Cocoa GUI アプリケーション
Cocoa GUI App
メモリ管理検証  new
Memory Management Test

Other
参考図書・グッズ
Favorites
ソフトウエア
Software  soon

Legacy
古い記事
Objective-C Primer
 Cocoa GUI App 第7回は一応完成しました。しかしまだ一度もコンパイルしておりません。それには色々な理由があるからですが、次回第8回が始まってから各コードに修正がでる可能性は非常に高いです。ご了承のほどよろしくお願いいたします。

コントローラークラスの作成

 Interface builder は保存して一旦閉じて頂いて結構です。モデルクラスを作った時と同じようにプロジェクトウィンドウの左側の「グループとファイル」の Classes フォルダを選択してファイルメニューから「新規ファイル...」を選びます。



 次の「新規ファイル」アシスタントで「Objective-C class」を選び次へを押します。

 ファイル名を ITController.m にして後はデフォルトのままで結構です。下の図では「保存場所」に「~/Desktop/Introducer」となっていますが、この部分はあなたのプロジェクトフォルダのある場所を Xcode が自動で検知して表示しています。下の図と違う場所が表示されていても問題はありません。ファイル名が記入できましたら「完了」ボタンを押してください。

ITController.h のコーディング

 上記の手順で ITController クラスを作成しましたら自動的に ITController.h ファイルが開くと思います。もし開いていないようでしたらプロジェクトウィンドウに出来上がった ITController.h をダブルクリックして開いてください。そして次のようにコーディングします

ITController.h


インスタンス変数の宣言部分

 12行目から33行目までの波括弧 { } の間に全部で15個のインスタンス変数を宣言しています。そしてそのうちの14個の冒頭に IBOutlet と記述されています。これは Objective-C の言語上の仕様ではありません。実際にこの IBOutlet という文字列はコンパイル時には除去されます。しかし Interface Builder 上ではこの IBOutlet という文字列が付いたものだけがそのオブジェクトが持っているインスタンス変数として表示されます。そしてこのインスタンス変数に他のオブジェクトを視覚的に登録(初期化)することが出来ます。このことを Interface Builder 上では接続すると呼びます。この接続方法については次の回で説明いたします。

 「他のオブジェクトを登録できる」あるいは「他のオブジェクトで初期化できる」というたぐいのインスタンス変数は参照とも呼びます。それに対して整数などのスカラー値と呼ばれる基本的な値を保持するインスタンス変数をプロパティと呼びます。今回この ITController クラスで定義されているインスタンス変数はすべて参照タイプになります。

 最後のインスタンス変数 model の先頭には IBOutlet という記述はありません。したがって Interface Builder はこのインスタンス変数の存在には気付きません。この変数の初期化はこの ITController クラスの awakeFromNib メソッドの中で行います。awakeFromNib メソッドについての説明は後ほどいたします。

 ここで説明しておかなければならないのはインスタンス変数のタイプ (型) が NSTextField * 型と NSButton * 型しかなかったことです。Label という GUI パーツは確かに Interface Builder 上では存在していますが、実際には NSTextField のインスタンスの属性値を変えただけのものです。従ってインスタンス変数の型としては NSTextField になります。これと同じように形の違う色々なボタンが存在しますがそのほとんどが同じ NSButton クラスのインスタンスです。

 各シンスタンス変数は、NSTextField 型のインスタンス変数の xxxxInfo がそれぞれのタブの一番上の Label に、xxxxName がそれぞれのタブの2番目の Text Field に、xxxxResult が3番目の Label に、そして xxxxMessage が4番目の Text Field に接続される予定になっています。xxxx の部分はそれぞれのタブ名と同じになります。xxxxButton については説明不要でしょう。

メソッドの宣言部分

 次にメソッドの宣言部分ですが戻り値の所に (IBAction) と記述されているものだけが Interface Builder で認識されます。認識されないものは当然接続もできません。38・39・40行目の各メソッドはそれぞれ Greeting ボタン・Addition ボタン・Deletion ボタンが押された時に呼び出される(メッセージが送信される)メソッドです。この (IBAction) の戻り値の付いたメソッドは必ず (id)sender という引数を持つことになっています。この sender 引数はメッセージの送り主(この場合ではそれぞれの Push Button) の参照になっています。したがってメソッドのほうからメッセージの送り主についての情報をこの参照を辿って取得したり、あるいは送り主の値をメソッドの方から設定することもできます。なおこの (IBAction) はコンパイル時に (void) に置き換えられます。したがってメソッドの呼び出し側(メッセージの送り主)になんらかの作用をもたらすためにもこの sender 引数は必須のものとなります。

35行目と36行目の notRegistered メソッドと registered メソッドは登録数がゼロの場合には Greeting ボタンと Deletion ボタンを使用できなくして(グレー表示にして)、登録が1つでもあれば Greeting ボタンと Deletion ボタンを使用できるようにするためのメソッドです。アプリケーション起動時(正確にはGUI構築時)とタブを切り替えた時にこのコントローラー内部で呼び出されるメソッドなので (void) 型にしました。したがってこのメソッドも Interface Builder は認識しません。

 43行目からデリゲートというメソッドが2つ宣言されています。コメントの通りタブ切替時に呼び出されるメソッドとアプリケーション終了時に呼び出されるメソッドです。このメソッドを呼び出しているのは(送信しているのは) NSTabView と NSApplication です。この第7回でこのデリゲートの説明が一番難しいものになると思いますが、じっくり読んでください。
 デリゲートは委任や委譲と翻訳されます。普通はあまり使わないであろうメソッド、あるいはどういう動作になるか想定しにくいと思われるメソッドを delegate という名前のインスタンス変数に接続したオブジェクトに実装する(実際に行う振る舞い決める)方法です。


デリゲート

 デリゲートはあるオブジェクトの通常の動作(デフォルトの動作)ではない動作を担当するメソッドを別のオブジェクトに宣言・実装して使用する方法です
 このデフォルト以外のメソッドを担当するオブジェクトはdelegateという名前のインスタンス変数に接続されることに決まっています。こ delegate に接続されるオブジェクトはほとんどの場合が自分で作成したクラス (独自クラス) になります

 それとは逆にこのデリゲートを呼び出す側の機能を独自クラスに搭載することは非常に難しい事です。しかし実際には多くのApplication Kit クラス、つまり多くの GUI パーツには最初からこのデリゲート機能が搭載されています

 具体例で示すとウィンドウのクローズボタンを押すとそのウィンドウは閉じます。これがウィンドウのデフォルトの動作です。しかしウィンドウに表示されているデータを保存しなければならないようなアプリケーション (例えばテキストエディットなど) ではdelegateに登録したオブジェクトに windowShouldClose: というメソッドを送ります。「ウィンドウは閉じるべきですか?」「ウィンドウを閉じても良いですか?」という意味のメソッドです。そしてこのメソッドが「データはすでに保存されている」などの理由によりウィンドウを閉じても良いと判断した場合にはYESという戻り値を返します。YES を受け取ったウィンドウオブジェクトは閉じられます。しかし未保存のデータがある場合は NO を返してウィンドウが閉じられる事をキャンセルします。あるいはダイアログボックスを表示してデータの保存をユーザーにうながすようにします。もちろんこのメソッドが呼び出された時点でデータを自動的に保存して YES を返しても構いません。というかこのように「メソッドを受け取ればデータを保存する」ことが決まっている場合などは windowWillClose: というデリゲートメソッドが用意されています。このメソッドはウィンドウが閉じる時に呼び出されてメソッドの内容を実行します。返り値は持っていません。そして呼び出し側のウィンドウは必ず閉じられます。


 上記のテキストエディットウィンドウはクローズボタンを押すと閉じられます


 上記のテキストエディットウィンドウはクローズボタンを押すと・・・


 ダイアログが表示されます。保存を押すとデータを保存してからウィンドウオブジェクトに YES を返します。保存しないを押すとデータを保存しないで YES を返します。キャンセルを押した場合はウィンドウオブジェクトに NO が返されます。

さらに詳しく

 既存のクラスにメソッドを追加する方法は色々とあります。まずその代表が当然のことながらクラスの継承でしょう。しかしデリゲートによるメソッドの追加には継承ではできない次のような利点があります

・実行時にデリゲートオブジェクトを切り替える事ができる

- (void)setDelegate:(id)anObject
- (id)delegate
というメソッドを使ってデリゲートオブジェクトの設定と取得ができます

・1つのオブジェクトを複数のオブジェクトのデリゲートに指定することができる

実際にこのサンプルプログラムでも ITController が NSTabView と NSApplication のデリゲートになっています

 デリゲートを指定しているオブジェクトは実行時にそのデリゲートがどんなメソッドを実装しているかを知らべます。実装されていないメソッドは送信されません。またデリゲートメソッドは必要なメソッドだけを宣言・実装しておけば良く、そのクラスが指定できるすべてデリゲートメソッドを記述する必要はありません。

 なお余談になりますが実装したいデリゲートメソッドはデリゲートとして指定されたクラスのインターフェースファイル(xxx.h)で宣言して、実装ファイル(xxx.m)で実装しなければないことになっています。しかし実際にはインターフェースでの宣言を省略してもたぶん正常にコンンパイルできて動作もします。これはデリゲートメソッドの多くが NSObject で宣言されているからだと思います。しかし仕様としては必ずデリゲートに指定されたクラスのヘッダファイルでメソッド宣言をしてください。

 最後になりましたが43行目から50行目の2つのメソッドはデリゲードメソッドとして決まっているメソッドの宣言です。従って一文一句同じように記述しなければなりません。しかし実装はプログラマの自由意思で記述できます。一応この2つのデリゲートメソッドの説明をしておくと
 1番目がタブビューでタブを切り替えた時に呼び出されるデリゲートメソッドです。デリゲートの持ち主はタブビューになります。2番目がアプリケーション終了時に呼びだれるデリゲートメソッドです。このデリゲートの持ち主は NSApplication と呼ばれるクラスのインスタンスです。NSApplication は Cocoa GUI アプリケーション全体を管理するオブジェクトで1つのアプリケーションに1つだけ存在します。NSApplication の主な仕事はアプリケーションの起動と終了・イベントループの管理・イベントの受け取りなどになります。


ITController.m のコーディング

ITController.m

 注意:この ITController.m は一発書きです。まだ一度もコンパイルしていません。どこかに記述ミスがある可能性のほうが高いですが、でもそれぞれの場面のスクリーンショット(画面)を撮っていくために敢えてこのまま進めることにします。当然途中でミス部分が出てくればその都度修正します


コード説明

awakeFromNib 12行目〜23行目

 GUI を構成するオブジェクトはそのアプリケーションが起動するまではコード化された状態で nib ファイルに保存されています。アプリケーションの起動と同時にこの nib ファイルにコード化されていたオブジェクト (GUI パーツなど) が元のオブジェクトの形に復元されてウィンドウやメニューなどが表示されます。この時に nib ファイルを構成しているオブジェクトに向けて一斉にこの awakeFromNib メソッドが送信されます。GUI パーツによっては最初から初期値 (例えば表示する文字列など) を与えておくこともできますが、この awakeFromNib メソッドを使って起動時に初期値を与えるほうが一般的でよりスマートです。

 ここでは14行目で model 変数に ITModel のオブジェクトを代入しています。そして15行目から18行目でそれぞれの TextField に setStringValue: メソッドを使って文字列を設定しています。どの TextField にどの文字列を設定するのかじっくりコードを追ってみてください。

 19行目で model オブジェクトの dictionary ゲッターメソッドで返ってくるオブジェクトに対して count メソッドを送っています。これは NSDictionary に登録されているエントリーの数を返します。そしてエントリーがなければ (登録数が0ならば) notRegistered メソッドを呼び出し、登録があれば registered メソッドが呼び出されます。

notRegistered および registered 25行目〜47行目

 setEnabled: メソッドは NSTextField (前述のとおり Label も含んでいます) や NSButton など多くの GUI オブジェクトで使えるメソッドです。引数には BOOL 値 (YES か NO) を指定します。YES の場合にはその GUI オブジェクトは使える状態になり、NO の場合にはその GUI オブジェクトはグレーで表示され使えなくなります。InterFace Builder で配置した場合は最初はすべて YES の値が与えられています。この notRegistered メソッドと registered メソッドでもどのインスタンス変数にどういう値 (文字列や BOOL 値) が与えられるのか追ってみてください。

greeting 49行目〜60行目

 52行目で model オブジェクトに showIntro メソッドを送信しています。モデルクラスの showIntro メソッドは引数と同じキーのエントリーの値を返します。ここでは [greetingName sringValue] で greetingName テキストフィールドに記入された文字列 (名前) を stringValue メソッドで取得してそれを sowIntro の引数としています。 stringValue メソッドは NSTextField の title 変数の値を取得するメソッドです。showIntro メソッドはキーと一致するエントリーがない場合には nil を返します。それを利用して if 文分岐しているのが53行目から60行目です。それぞれのケースを追ってみてください。
 なお model オブジェクトにエントリーが1つもない場合には Greeting ボタンはグレー表示になって使えなくなっていますから、もとからこのメソッドが呼び出されることはありません。

addition 62行目〜74行目

 additionName テキストフィールドの文字列と additionMessage テキストフィールドの文字列をそれぞれローカル変数 name と message に代入してそれを引数として model オブジェクトの addIntro: name: メソッドを送信しています。この addIntoro: name: メソッドは新しいエントリーが登録できた場合には YES を、そうでなかった場合には NO を返します。それを利用して67行目から if 文分岐しています。69行目では [self registered] メッセージを送信していることに注意してください。
 なお、addIntro: name: メソッドの詳しい実装は   Cocoa GUI App 第3回 モデル をご覧になってください。

deletion 74行目〜85行目

 deletionName テキストフィールドの文字列を model オブジェクトの removeIntro: メソッドの引数にしてメッセージを送信しています。この removeIntoro: メソッドはキーと合致するエントリーをディkショナリーから削除します。そして削除できた場合には YES を、そうでなかった場合には NO を返します。それを利用して78行目から if 文分岐しています。80行目〜81行目でディクショナリーのエントリー数を計り直してもし 0 だった場合には自己の notRegistered メソッドを実行していることに注意してください
 なお、removeIntro: メソッドの詳しい実装は   Cocoa GUI App 第3回 モデル をご覧になってください。

tabView: willSelectTabViewItem: 87行目〜97行目

 NSTabView が持っているデリゲートメソッドです。どれかのタブが選ばれた時に送信されるメソッドです。実装のほうはプログラマに任されます。どのようなことを行っているかサンプルコードを追ってみてください

applicationWillTerminate 99行目〜102行目

 NSApplication が持っているデリゲートです。アプリケーションが終了する時に送信されるメソッドです。ここでは model オブジェクトの writeFile メソッドを呼び出しています。writeFile メソッドはこの Introducer に登録された自己紹介文をファイルに保存するメソッドです。また NSApplication は Cocoa GUI アプリケーションでは必ず1つ持つことになっているオブジェクトでプログラマが意識しなくとも「新規オブジェクト...」を選んだ場合には自動で作成されています。
 なお、writeFile メソッドの詳しい実装は   Cocoa GUI App 第3回 モデル をご覧になってください。





 お疲れさまでした。これで Cocoa GUI App 第7回は終ります。
この回で、Interface Builder で行うビューオブジェクトとコントローラーオブジェクトの接続も一気に説明したいと思っていましたが、今回が長くなってしまったので次回の第8回を「接続」というタイトルにしてこの接続方法をスクリーンショットをふんだんに使いながら説明したいと思います。
 第4回から第6回では Interface Builder での GUI の作成を行いましたが、次回の「接続」も Interface Builder の重要作業です。そしてそれが終ればいよいよアプリケーションが動作するようになります。

目次を表示 (先頭へ戻る) 前ページ   次ページ

This site is available in Safari and Leopard. © ttezu 2006 - 2008