ページ

2009年12月2日水曜日

NSTableView にカスタムビューを表示する (7) NSCollectionView

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

(前回)Cocoaの日々: NSTableView にカスタムビューを表示する (6)カスタムセルにモデルオブジェクトの内容を描画する

前回カスタムセルで画像と文字列を描画したがこれを Interface Builder でデザインしたい。


カスタムセル VS カスタムビュー

同じ事を考える人がいて Stack Overflow に質問が出ていた。が、やはり難しそうだ。
Is it possible to design NSCell subclasses in Interface Builder? - Stack Overflow


となると通常通りカスタムビューを作成してデザインするしかない。その場合、カスタムセルでそのカスタムビュー(Nibになる)を読み込んで描画することになる。ただ NSTableView の上のセルの上に NSView を載せることになり、複雑な上にちょっと無理矢理感がある(そもそも NSViewの重さを嫌って NSCell を使っているのに NSView を載せるというのは本末転倒ともいえる)。NSTableView を使いたいがためにこうなっているが、ここまでくると NSTableView をやめて自前で表組を作った方がいい。って、まさその用途向けのクラスが NSCollectionView か。

1行に複数プロパティを表示する方法について、ここまでを整理すると:

(方法1)NSTableViewでカスタムセルを使う
  ○ NSViewに比べて軽いので大量データの表示に向く。NSTableView の機能が使える。
  × 自前で描画とレイアウトのコードを書く必要がある。
    表示のみで入力ができない(セルにNSTextFieldを無理矢理貼付ける方法もあるが...)。

(方法2)NSCollectionViewを使う
  ○ InterfaceBuilderを使ってレイアウトできる。Cocoa Bindings が簡単に扱える。
    入力もできる。
  × すべて NSView による構成となるので表示アイテムが大量になった場合重そう。
    NSTableView の機能が使えない。


どちらも長所短所があり、だいたい正反対の正確を持つ。


サンプル作成


NSColectionView が NSTableView的に使えれば代用になるかもしれない(今のところ NSTableView にこだわりは無いので)。サンプルを作って試してみよう。


NSCollectionView は過去に検証したことがあるのでこれを参考にする。
Cocoaの日々: NSCollectionView

前回の nibに NSCollectionView(scrollbar付き)を追加する。すると3つのオブジェクトが追加される。

新規にウィンドウを作成し、先ほど自動生成された Bordered Scroll View (Collection View) を貼付ける(張り付け後にこのオブジェクトは nibに一覧から消える)。

次に自動生成された View Item を開き、NSImageView と NSTextFieldを配置する。このビューが NSCollectionView の1要素となる。

続いて bindings 設定。Collection View Item がコントローラーの役割を果たすので、この中の representedObject を指す様にして設定する。まずは NSTextField。

続いて NSImageView。

representedObject.image はファイル名(NSString)なのでこれを NSImage に変換する必要がある。ここでは ImageTransformer を用意してこの変換(ファイル名=>NSImage)を行わせている。

ImageTransformer.m

@implementation ImageTransformer
+ (Class)transformedValueClass
{
    return [NSImage class];
}

- (id)transformedValue:(id)value
{
if (!value) {
return  nil;
}
NSImage* image = nil;
NSString* path = [[NSBundle mainBundle] pathForImageResource:value];
if (path) {
image = [[NSImage alloc] initWithContentsOfFile:path];
}
return image;
}

+ (BOOL)allowsReverseTransformation
{
return NO;
}

@end



(参考)



最後に NSCollectionView の Bindings設定。表示内容であるモデルを紐づける必要があり、ここでは Content を前回から使っている Array Controller の arrangedObjects へ設定している。


なお Bind to: にチェクを入れるとデフォルトでは Controller Key が arrangedObject ではなく selection になっている。そのままだと実行時にエラーになるので注意。


実行結果

動かしてみよう。

出た。



ソースコード

github からどうぞ。
CustomCell at 20091202-2 from xcatsan's SampleCode - GitHub





- - - - - -


ImageTransformer を除けばコードは書いていない。Xcodeでモデルさえ用意しておけば Interface Builder での設定だけで動いてしまう。この仕組みは本当によくできている。Bindings を使った MVC に慣れてくると Cocoaプログラミングは俄然楽しい。