前回の記事で(→「設定の保存にまつわる問題 - 環境設定パネルを作る (MacBloggerGlass)」)、環境設定パネルの表示状態を保存・復元するためには、表部分の選択もモデルとして保持しなければならない、と書いた。
そこで、今回の内容は、NSTableView で選択されるべき行をモデルに格納する方法。もちろん、両者を結びつけるには Cocoa Bindings を使う。
まず、考えるべきは表の選択(に関する情報)を、Cocoa Bindings ではどう扱えば良いのか? 表 (NSTableView) とモデルを結びつけているのは NSArrayController だ。ならば、選択のための結びつきも NSArrayController で用意されているはず。
NSArrayController の Selection Indexes
NSArrayController の「Bindings」パネルを見てみると、それらしい項目が見つかる。それが「Selection Indexes」だ。右のスクリーンショットは「Selection Indexes」を開いたところ。この例では結びつける先は「File's Owner」(この NIB をロードするオブジェクト)の selectionIndex というプロパティになっている。
さて、これで選択のための結びつきを作れることはわかった。次の問題は選択を表現するデータの型は何かということ。NSTableView で選択された行は selectedRow メソッドで取り出すことができるが、この型は NSInteger で、これはつまり int だ。では、上記の結びつきの先となるプロパティは int (あるいは NSInteger) で良いのか?
Interface Builder 上で Selection Indexes のところにマウスを持っていき、しばらくじっとしているとツールチップが表示される。そこには NSIndexSet のインスタンスが選択された行を指定すると書かれている。もっと詳しく知りたければ「Cocoa Bindings Reference」の NSArrayController > Controller Content Parameters Bindings > selectionIndexes を見ること。
サンプルコード
NSTableView の選択を Cocoa Bindings を使ってモデルと結びつける実例として、MacBloggerGlass の環境設定パネルを実現するコードからの抜粋を示す。
まず、モデルとしての NSIndexSet は PreferenceController オブジェクトに抱えさせている。プロパティ名は先のスクリーンショットにも出ていたように selectionIndex だ。
PreferenceController の実装で selectionIndex にアクセスするのは(解放する dealloc を除いて) 3 ヶ所。
- プロパティとしてのアクセサ(readonly)
- windowDidLoad 内でインスタンスの確保と初期化(空選択として)
- ブログ一覧を取得した際に実行される blogListTicket:finishedWithFeed:error で User Defaults に保存された選択の復元
NSIndexSet にはインスタンス生成用のクラスメソッドが用意されていて、空選択の場合には +indexSet を用いる。
保存された選択の復元を行うコードを以下に示す。
処理の内容は、(a) User Defaults から「選択された Blog ID」を取り出し、(b) ブログ一覧の各項目の ID と比較し、(c) 一致するものが見つかれば、(d) それを選択する、となっている。
- (void)blogListTicket:(GDataServiceTicket *)ticket
finishedWithFeed:(GDataFeedBlog *)aFeed
error:(NSError *)error
{
[...snip...]
// selectedIndex
NSString *blogID = [self selectedBlogID]; // ........................ (a)
int index = 0;
EntryBlog *entry;
for (entry in feed.entries) {
if ([entry.blogID compare:blogID] == NSOrderedSame) break; // ... (b)
index++;
}
if (index < [feed.entries count]) { // ........................... (c)
[self willChangeValueForKey:@"selectionIndex"];
[selectionIndex initWithIndex:index]; // ........................ (d)
[self didChangeValueForKey:@"selectionIndex"];
} else {
[selectionIndex init]; // empty NSIndexSet
}
}
(d) の前後にある willChangeValueForKey: と didChangeValueForKey は KVO の通知を送るためのお作法の 1 つ(→「表示するブログの選択 - 環境設定パネルを作る (MacBloggerGlass)」を参照)。
今さらなんだけど、NSString って == で(文字列としての)比較はできないのな。Ruby や Python に慣れていると、つい == で比較してしまうよ。
関連リンク
- NSIndexSet Class Reference (Mac OS X Reference Library)
- Cocoa Bindings Reference (同上; "Bindings" には Cocoa のクラスごとにどのような結びつきが作れるかの完全なリストと説明がある)
0 件のコメント:
コメントを投稿