● Carbon視点でCocoa探求(2008/09/08)

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

 〜 datasourceとdelegate 〜


Cocoaの場合、Interface Builderでの設定がソースコードと密接に関連しているため作業をより複雑にしています。このために、実際のプログラミング作業に入る前にめげてしまう人もいるかもしれませんが、 今回は、ちゃんとしたソースコードの話です(笑)。

NSTableViewの場合と同様、ImageKitのIKImageBrowserViewでは、MyDocumentクラスに実装した「datasource」と「delegate」メソッドが、画像表示のメンテナンスやユーザからの操作(画像クリックなど)に対応します。このうち、datasourceでは画像情報が保存されたモデルオブジェクトやその個数を返す事になります。今回のモデルオブジェクトは、以前に作成したImageFileクラスのオブジェクト(ファイルパス名などを含む)です。まず最初に、これらを保存する配列(NSMutableArray)と、画像を読み込む時に一時的に用いる配列を、 MyDocumentクラスのインスタンス変数として用意しておきます。MyDocumentクラスのクラス定義は以下のようになります。

@interface MyDocument : NSDocument
{
 IBOutlet id    imageBrowser;  // IKImageBrowserViewのOutlet
 IBOutlet id    imageView;   // NSImageViewのOutlet
 NSMutableArray *list;       // Idatasourceで利用
 NSMutableArray *temp;      // ファイル作業用テンポラリー
}

まず最初は、MyDocumentクラスに実装するdatasourceです。これについては「登録数を返す」「指定番号の画像情報を含んだImageFileオブジェクトを返す」「指定番号のImageFileオブジェクトを削除する」「画像の順序を入れ換えた時の後処理を行う」の5つのメソッドを用意します。この中で少し複雑なのは、最後の「 画像の順序を入れ換えた時の後処理を行う」ぐらいで、残りは大変簡単な処理で済むことが分かります。

- (int)numberOfItemsInImageBrowser:(IKImageBrowserView*)view
{
 return [list count]; // 画像の登録数を返す
}

-(id)imageBrowser:(IKImageBrowserView *) view itemAtIndex:(int) index
{
 return [list objectAtIndex:index]; // ImageFileオブジェクトを返す
}
- (void)imageBrowser:(IKImageBrowserView*)view
             removeItemsAtIndexes: (NSIndexSet*)indexes
{
 [list removeObjectsAtIndexes:indexes]; // ImageFileオブジェクトを削除
}

-(BOOL)imageBrowser:(IKImageBrowserView*)view
  moveItemsAtIndexes: (NSIndexSet*)indexes toIndex:(unsigned int)dindex
{
 BOOL      ret=NO;  // 画像の順序を入れ換えた時の後処理を行う
 NSMutableArray *temp;
 NSInteger     ct,i;
 id        obj;

 if( temp=[[NSMutableArray alloc] init] )
 {
   for(i=[indexes lastIndex];i!= NSNotFound;i=[indexes indexLessThanIndex:i])
   {
     if( i < dindex )
       dindex--;
     obj=[list objectAtIndex:i];
     [temp addObject:obj]; // 一時的に保存
     [list removeObjectAtIndex:i]; // オブジェクトを削除
   }
   ct=[temp count];
   for( i=0;i<ct;i++ )
    [list insertObject:[temp objectAtIndex:i] atIndex:dindex]; // 挿入
   [temp release];
   ret=YES;
 }
 return ret;
}

NSTableViewならこれだけですが、IKImageBrowserViewの場合には、もう少し追加作業が必要となります。ImageFileオブジェクトを渡した時に、その中のどの種類の情報(インスタンス変数)を画像表示用に使うのかという指示が必要なのです。その処理に準拠したdatasourceを、ImageFile(モデル)クラス側に実装します。具体的には「情報の種類」「情報の内容」「識別子(ID)」「画像タイトル」「画像サブタイトル」を返す5つのメソッドを用意することになります。 以下がそうしたdatasourceの実例です。


- (NSString*)imageRepresentationType // 画像表示にどんな種類の情報を使うか
{
 if( [type isEqualToString: @"public.movie"] ) // Movieファイル
   return IKImageBrowserQTMoviePathRepresentationType;
 else if( [type isEqualToString: @"public.executable"] ) // CCompositionファイル
   return IKImageBrowserQCCompositionPathRepresentationType;
 else                   // 一般画像ファイル
   return IKImageBrowserPathRepresentationType; // 画像ファイルパスを使う
}

- (id)imageRepresentation // 画像表示用の情報を返す(画像ファイルパス名)
{
 return path;
}

- (NSString*)imageUID // 画像の固有識別子を返す(画像ファイルパス名と一致)
{
 return path;
}

- (NSString *) imageTitle; // 画像タイトルを返す(今回はファイル名)
{
 return[ path lastPathComponent]; // パス名の一番最後を抽出
}

- (NSString *) imageSubtitle; // 画像サブタイトルを返す(今回はファイルタイプ)
{
 return type; // タイプにはファイル種類識別用のメタデータが入っている
}

次はdelegateメソッドですが、こちらはMyDocumentクラスに実装します。以下は、ユーザがブラウザ上の任意の画像をマウスクリックした時に実行されるdelegateです。マウスクリックにより、その画像をお隣のNSImageViewいっぱいに表示します。

- (void) imageBrowserSelectionDidChange:(IKImageBrowserView *) aBrowser
{
 NSImage   *image;
 NSIndexSet  *set;
 ImageFile   *obj;

 if( set=[imageBrowser selectionIndexes] ) // 選択画像のインデックスを得る
 {
   if( obj=[list objectAtIndex:[set firstIndex]] ) // ImageFileオブジェクトを得る
   {
     if( image=[[NSImage alloc] initByReferencingFile:obj.path ] )
     {               // パス名を使い画像(NSImage)を得る
       [imageView setImage:nil];
       [imageView setImage:image]; // 得られた画像をNSImageViewに設定
       [image release];
     }
   }
 }
}

その他に「画像をダブルクリックした時の処理」「画像を右ボタンでクリックした時の処理」「背景を右ボタンクリックした時の処理」なども、delegateとして実装することが可能です(以下参照)。ちなみに、画像のダブルクリックに対する処理を差し替えなければ(delegateを実装しなければ)デフォルトとして、アプリケーション(オーナ)による画像のオープンが実行されます。

- (void) imageBrowser:(IKImageBrowserView *) aBrowser
        cellWasDoubleClickedAtIndex:(NSUInteger) index

- (void) imageBrowser:(IKImageBrowserView *) aBrowser
  cellWasRightClickedAtIndex:(NSUInteger) index withEvent:(NSEvent *) event

- (void) imageBrowser:(IKImageBrowserView *) aBrowser
backgroundWasRightClickedWithEvent:(NSEvent *) event;

画像さえ登録してしまえば、ImageBrowserの扱いは簡単そうに見えます。ところが、その画像登録に色々なパタンが存在するので注意が必要です。次回は「しんぶんし3」でのドキュメントファイルや画像ファイルの取り扱いを復習しながら、登録ボタンで画像を追加していく処理を解説したいと思います。


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