【Cocoa】NSTableViewのお勉強
バインディングの記事を書きたかったが、あまりに俺の頭がついて行けてないので、いったん保留することに決定。煮詰まりそうだ・・・難しく考えすぎてる?
それで、今度はテーブルビューの使い方を勉強した。クラス名は「NSTableView」。BorlandのVCLだと「TStringGrid」に相当する部品。行と列があって、それぞれのマス目に文字列を表示できる。
勉強のネタ本にしている「たのしいCocoaプログラミング」には、「文字列以外に画像やボタンなども入れることができる」と書いてある。スゲー。
■NSTableViewの基本
使い方というかテーブル表示の実現の仕方が、VCLのTStringGridとはまるで異なっているようだ。
まず表示するデータはNSTableViewのオブジェクトに突っ込んでいくようなことはしないらしい。あくまでNSTableViewは見た目の表示部分だけを司り、表示内容となるデータは別個用意する必要があり、そのデータ形式はどんなんでもいいらしい。
ではNSTableViewは、別個に用意された形式不明のデータをどうやって表示するのかというと、NSTaableViewから来る(最低限)二つの問い合わせに答えるプログラムを用意することで実現しているらしいのだ。
二つの問い合わせとは「データは何行あるのか」と「X列Y行のデータは何か」というもの。
たとえ1万行あるデータであったとしても、実際にウインドウ上に5行しか表示しないにであれば、この問い合わせは5回しか発生せず、スクロールして新たな行を表示する必要が出た時点で、その分だけしか問い合わせされないというのだ。
その二つの問い合わせとは、以下の二つのメソッドになる。
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
- (id)tableView:(NSTableView *)aTableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn
row:(NSInteger)rowIndex
この二つは「NSTableDataSource」プロトコルに定義されているメソッドで、他にも「ソートの昇順降順が入れ替わりました」とか「X列Y行にデータが書かれました」とかいったメソッドがある。
これらのメソッドは「クラス」ではなく「プロトコル」として定義されているので、このメソッドを使用するために特定のクラスを継承したりする必要はなく、何かしらのオブジェクトにこの名前のメソッドを実装するだけでいいらしい。それがプロトコルという物だそうだ。
■実践
簡単な例で使用方法を確認してみた。
目指すところは、2列7行のテーブル表示。1列目は0〜6までの数字を表示する。2列目には「Sunday」〜「Saturday」までの7つの曜日を表示する。
1.プロジェクトの生成
いつものようにXcodeを起動。新規プロジェクトで「Cocoa Application」を選択。プロジェクト名は「TableView」にした。
2.InterfaceBuilderを起動
ResourceにあるMainMenu.xibをダブルクリックしてInterfaceBuilderを起動する。
3.コントローラオブジェクトの用意
まずはコントローラオブジェクトを用意する。Libraryから「NSObject」を「MainMenu.xib」ウインドウにドラッグ&ドロップ。Identityウインドウでクラス名として「AppController」と命名しておく。
今回はアクションもアウトレットも使わないので、このままクラスファイルとして出力してしまう。MainMenu.xibウインドウの 「AppController」を選択した状態で、メニューの「File」→「Write Class Files ...」をクリック。ファイルを保存する。ファイル名は「AppController.m」ヘッダファイルもチェックを入れておく。
4.ウインドウにテーブルビューを配置
続いてウインドウ上にテーブルビューを配置する。部品自体はLibraryの「Cocoa」→「Views&Cells」→「Data Views」の中にあるので、そいつをウインドウにドラッグ&ドロップ。
5.データソースの接続
今回のプロジェクトでデータソースの役をするのは、先程作成した「AppController」ということにする。つまり、テーブルビューはAppControllerに対して「何行あるのか」とか「X列Y行のデータは何か」という問い合わせをするように仕向ける。
やり方はアクションなどを接続するときと同様。ウインドウ上のテーブルビューを選択した状態で、「control+ドラッグ」で「AppController」にドロップし、表示される接続候補から「dataSource」を選ぶ。
このとき少しコツがいる。
最初に選択するテーブルビュー側なんだが、部品を一回クリックするだけでは上記のような操作はできない。Identityウインドウのクラス名のところを見ると「NSScrollView」が選択されてしまっているのだ。
テーブルビューが選択されている状態で、もう一度テーブルビューをクリックすると、「NSTableView」をうまく選択できる。この状態でないといけない。
だからといって、テーブルビューをダブルクリックすると、「NSTableColumn」が選択されてしまう。ダブルクリックをするのではなくクリックを2回、だ。
6.列に識別子を付ける
これは、実際に「X列Y行のデータは何か」の問い合わせが合った際に、行は整数型で問い合わせが来るんだけど、列の方はここで設定する識別子で来ることになるらしいので、ここで設定する。
今度はテーブルビューをダブルクリックして「NSTableColumn」を選択する。テーブルに列は2つあるはずなので、それぞれの列をダブルクリックで選択して、識別しを設定する。
設定する箇所は、「Attributes」ウインドウの「Identifier」。
ここでは単純に「col1」、「col2」としておいた。
ここまでできたらInterfaceBuilderを終了、データを保存しておく。
7.ヘッダファイルの記述
Xcodeに戻って、「AppController.h」にいくつか記述を追加する。一つは親クラス。いつものように「NSObject」にする。もう一つは、テーブルビューからの問い合わせに対する応答をするための二つのメソッド宣言の追加。
【AppController.h】
#import <Cocoa/Cocoa.h>
@interface AppController : NSObject{
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView;
- (id)tableView:(NSTableView *)aTableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn
row:(NSInteger)rowIndex;
@end
8.メソッドの実装その1(データは何行あるのか)
次は「AppController.m」の記述。
まず「データは何行あるか」の問い合わせに応答する「numberOfRowsInTableView」メソッドだ。今回は7行固定なので、
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
return 7;
}
とした。これが例えば配列クラスをデータソースにしていて、データ数も可変な場合は、NSArrayクラスのcountメソッドをreturnしてあげるなどで応用できる。
9.メソッドの実装その2(X列Y行のデータは何か)
次に、「X列Y行のデータは何か」に応答する「tableView:objectValueForTableColumn:row:」メソッドだ。
行の方はrowで指定されるNSInteger型の変数に格納されてくるので、そのまま使えそう。
列の方はobjectValueForTableColumnで指定される変数を使うのだが、こちらは「NSTableColumn」オブジェクトへのポインタとなっている。要は列を表すオブジェクトだな、これ。さっきテーブルビューをダブルクリックして選択したやつだ。
この列のオブジェクトから識別子を取り出すには、「identifier」メソッドを使用する。さらに、例えばその列の識別子が「col1」と等しいかどうかを判断するには、「isEqual」メソッドを使って「@"col1"」と比較すればいい。
1列目なら、rowIndexで渡された0〜6までの数値を文字列にして返す。
2列目なら、rowIndexで渡された0〜6に対応する曜日の文字列を返す。
というわけで、ソースはこんな風になった。
【AppController.m】
#import "AppController.h"
@implementation AppController
//-------------------------------------------------------
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
{
return 7;
}
//-------------------------------------------------------
- (id)tableView:(NSTableView *)aTableView
objectValueForTableColumn:(NSTableColumn *)aTableColumn
row:(NSInteger)rowIndex
{
if([[aTableColumn identifier] isEqual:@"col1"]){
//1列目の場合
//rowIndexをそのまま文字列にして返す
return [NSString stringWithFormat:@"%d",rowIndex];
}
else if([[aTableColumn identifier] isEqual:@"col2"]){
//2列目の場合
//0:Sunday 1:Monday.....を返す
switch(rowIndex){
case 0:
return @"Sunday";break;
case 1:
return @"Monday";break;
case 2:
return @"Tuesday";break;
case 3:
return @"Wednesday";break;
case 4:
return @"Thursday";break;
case 5:
return @"Friday";break;
case 6:
return @"Saturday";break;
default:
return @"unknown";break;
}
}
else{
//それ以外の場合(ここは処理しないはず)
return @"";
}
}
//-------------------------------------------------------
@end
できあがりはこんな感じ。






コメント