● Carbon視点でCocoa探求(2008/06/18)

  このニュースは、MOSAの会員にのみ配布されているデベロッパー向けの
  デジタルマガジンMOSADeNのに掲載された記事です。ほぼ一ヶ月遅れで
  ここに掲載されて行きます

 〜 NSTableViewクラスを試してみる 〜


前回は、ドキュメントや画像ファイルについて実装すべき処理を列挙してみました。ただし、先んじてウィンドウに画像を表示する仕組みを作らないと、こうしたファイル処理の動作確認は困難です。今回は、登録された画像ファイルの内容をウィンドウに表示する方法について色々と考察してみたいと思います。

以前の「しんぶんし 2」では、こうした画像表示をCarbon FrameworkのDataBrowserコントロールを利用することで実現していました。用意したカラムの種類は、ファイル名とファイルの種類(OSType)の2種類のみですが、ファイル名の左側に画像アイコン表示を加えていました。しかし、このアイコン表示があまりにも小さいために(笑)一覧をざっと見て必要な画像を見極めるという役割にはあまり適していませんでした。

CocoaのAppKit FrameworkでCarbonのDataBrowserに準拠するものと言えば、それはNSTableViewクラスとなります。このクラスから作成されるTableViewコントロールのカラムには、ファイル名(パス名)やファイルタイプだけでなく、画像ファイルから得られたNSImageも表示できますので、CarbonでDataBrowserを実装した時とほとんど同じ状況を再現できます。カラムに表示する画像を通常よりも大きめ(カラムの高さを変更する)にしてやれば、選択の見極めにも有効かもしれません。

NSTableViewクラスを利用することは、そんなに難しくありません。Interface Builderでウィンドウ上にTableViewを配置して各種アトリビュート設定を行う点は、CarbonのDataBrowserの場合とほとんど同じです。DataBrowserの場合には、コールバックルーチン経由でカラムに表示するデータ(文字列など)を渡します。また、選択カラムが変更された情報や、カラムがダブルクリックされた時のイベントなどもコールバックルーチンで受け取る仕組みが用意されています。例えば、以下の様なソースコードを記述することで、DataBrowserにそうしたコールバックルーチンを設定してやります。

OSErr setupDataBrowser( ControlRef browser )
{
  OSErr          err=1;
  DataBrowserCallbacks  call;

  call.version=kDataBrowserLatestCallbacks;
  if( ! InitDataBrowserCallbacks( &call ) )
  {
    call.u.v1.itemDataCallback=setgetDataBrowser; // データの入出力
    call.u.v1.itemNotificationCallback=notifiDataBrowser; // イベント受信
    SetDataBrowserCallbacks( browser,&call ); // コールバックルーチン設定
    err=noErr;
  }
  return( err );
}

setgetDataBrowserの方が、DataBrowserに表示用のデータを渡す(もしくはBrowser側で編集されたデータを受け取る)コールバックルーチンです。notifiDataBrowserの方は、DataBrowser上で発生したイベント情報(マウスクリックによるカラム選択など)を受け取るコールバックルーチンです。DataBrowserのカラムに、パス名やファイルタイプを表示するだけであれば(内容の編集しない)、setgetDataBrowserコールバックルーチンは以下の様な感じとなります。ここでのgetMyData()ルーチンは、指定行番号のデータを得るための自作ルーチンです。

OSStatus setgetDataBrowser( ControlRef browser,DataBrowserItemID itemID,DataBrowserPropertyID property,DataBrowserItemDataRef itemData,Boolean changeValue )
{
  CFStringRef  path,type;
  short      err=noErr;

  if( ! changeValue )
  {
    if( itemID )
      getMyData( itemID-1,&path,&type ); // 指定行番号のデータを得る
    switch( property )
    {
      case 'path': // パス名の表示

        SetDataBrowserItemDataText( itemData,path );
        break;

      case 'type': // タイプ名の表示

        SetDataBrowserItemDataText( itemData,type );
        break;
    }
  }
  return( err );
}

表示対象の切り分けに使われるproperty値(DataBrowserPropertyID)は、Interface Builderで各カラムに対し設定しておいたOSType値です。ちなみに、Interface Builder 3では、DataBrowserの各種設定が非常に設定しづらく改悪されています(涙)何とかして欲しいと思うのですが...今回はとりあえず横に置いといて、notifiDataBrowserコールバックルーチンの例も見てみることにします。

void noteDataBrowserMosa( ControlRef browser,DataBrowserItemID itemID,DataBrowserItemNotification message )
{
  DataBrowserItemID  item;
  WindowRef      wptr;

  wptr=GetControlOwner( browser ); // ブラウザを配置したウィンドウ
  switch( message )
  {
    case kDataBrowserSelectionSetChanged:

      // ここにカラムが選択された時の処理を記述する
      break;

    case kDataBrowserItemDoubleClicked

      // ここにカラムがダブルクリックされた時の処理を記述する
      break;
  }
}

TableViewを使う時も、これらのコールバックルーチンと同様の役割(完全に同等ではない)を持つメソッドを実装する必要があります。TableViewへのデータ入出力については、dataSource(データソース)として選んだクラスに指定メソッドを実装しておきます。TableViewに対するイベントに対しては、delegate(デリゲート)として選んだクラスに必要なメソッドを実装します。 例えば「しんぶんし 3」用の「Cocoa Document-based」プロジェクトであれば、とりえあず、NSDocumentを継承したMyDocumentクラスをdataSourceやdelegateとして指定すれば大丈夫です。

TableViewのOutlet(アウトレット)として用意されているdataSourceとdelegateのリンク先は、他のアウトレット同様、Interface Builderにより線で接続することで設定します。TableViewを選択し、Inspector(インスペクタ)の「Table View Connection」の「Outlet」に表示されている、dataSourceとdelegateを好みのオブジェクト(青い立方体)へと接続します。先ほど言及したMyDocumentであれば「File's Owner」に接続することになります。どんな目的に対して、どのような種類のdataSourceやdelegateメソッドがあるかについては、技術ドキュメント「Table View Programming Guide」や、ヘッダファイルの「NSTableView.h」を参照してください。 まずは、最小限必要なふたつのdataSourceメソッドを紹介しておきます。これらは、表示すべき行の総数を返すメソッドと、その行番号へ表示する内容(オブジェクト)を返すメソッドです。

- (int)numberOfRowsInTableView:(NSTableView *)aTableView
{
  return [array count]; // テーブルが表示する全行数を返す
}

(id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTbableColumn row:(int)rowIndex
{
  ImageFile  *image;
  NSString  *ident;

  ident=[aTbableColumn identifier];  // 表示対象となるカラムのキーを得る
  image=[list objectAtIndex:rowIndex]; // 配列から対象行のオブジェクトを得る
  return [image valueForKey:ident];  // キーから得た表示用 プロパティを渡す
}

続いて、delegateメソッドであるtableViewSelectionIsChanging:を紹介しておきます。このメソッドは、ユーザにより選択カラムが変更された時に呼び出されます。delegateメソッドの方は、そのアプリケーションがどんな機能を実現したいかにより、実装すべき種類が大きく変わります。

- (void)tableViewSelectionIsChanging:(NSNotification *)aNotification
{
  long  row;

  row=[table selectedRow]; // 選択された行番号(ゼロ以上)
  if( row>=0 )
  {
    // ここには選択カラムの内容を利用する処理を記述する
    // 例えば別のTextFieldに選択内容を代入するなど...
  }
}

今回は、データ表示にdataSourceメソッドを使う方法を解説しましたが、TableViewでデータ表示する場合には、Mac OS X 10.3から導入された「Cocoa Binding」の仕組みを使うと便利です。この場合には、上記のようなdataSourceメソッドを用意することなく、データ(オブジェクト)のプロパティ値(インスタンス変数)をカラムに表示できます。 ただし、こちらの仕組みについては説明すべき技術項目が膨大ですので、機会があれば、別途「Cocoa Binding」に特化した解説を行いたいと思います。

さて、試しにTableViewを利用して領域を広げたカラムに画像(NSImage)を表示してみると、それなりに使えることが分かりました。しかし、画像に付随したパス名などが横の矩形枠の中に小さく表示され、間の抜けた感じが拭えません。やはり、TableViewは画像中心の一覧表示には向いているとは言い難いようです。 画像と、それに付随する情報を美しく一覧表示するのには、何か別の「ブラウザ機能」を採用した方が良さそうです。

そこで次回は、TableViewの代わりに、Mac OS X 10.5から採用された「Image Kit」のIKImageBrowserView(画像ブラウザ機能)クラスを利用してみます。ImageBrowserと名称が付いているわけですから、今回の様な役割にはもってこいだと予想されますが、さてどうなるでしょうか?


copyright 2008 Ottimo, Inc. All rights reserved
無断転載・引用禁止