2010-11-30

Cocoa Bindings の使い方 #3 - 選択を結びつける

前回の記事で(→「設定の保存にまつわる問題 - 環境設定パネルを作る (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 に慣れていると、つい == で比較してしまうよ。

関連リンク

関連記事

0 件のコメント:

コメントを投稿