Controllerと たわむれる12-3

前回のつづき


第三弾です。

第三弾は、QLPreviewerPanelを開く時、閉じる時にzoom効果を付けるです。
まあ、Delegateメソッド実装するだけなんですけど。


共通部分がないわけではないんですけど、まとめるって程でもないので各サブクラスでそれぞれ実装します。
実装するDelegateメソッドは
- (NSRect)previewPanel:sourceFrameOnScreenForPreviewItem:
です。なんと言うかそのままですね。
ズーム開始前、終了後のイメージのフレームを返します。問題はOnScreenってとこですね。Screen座標系で返す必要があります。


まずは一番簡単なControllersFlowViewControllerから。

ControllersFlowViewController.mの一部

- (NSRect)previewPanel:(QLPreviewPanel *)panel sourceFrameOnScreenForPreviewItem:(id <QLPreviewItem>)item
{
    NSRect selectionFrame = [coverFlow selectedImageFrame];
    
    selectionFrame = [coverFlow convertRectToBase:selectionFrame];
    selectionFrame.origin = [[coverFlow window] convertBaseToScreen:selectionFrame.origin];
    return selectionFrame;
}

ずばり -[IKImageFlowView selectedImageFrame]メソッドが選択中のアイテムのフレームを返してくれます。
これをScreen座標にしてやれば良いだけです。
簡単ですね。


続いてControllersIconViewController。

ControllersIconViewController.mの一部

- (NSRect)previewPanel:(QLPreviewPanel *)panel sourceFrameOnScreenForPreviewItem:(id <QLPreviewItem>)item
{
    NSUInteger selectionIndex = [[self representedObject] selectionIndex];
    if(selectionIndex == NSNotFound) return NSZeroRect;
    id imageView = [self previewTragetView];
    NSRect selectionFrame = [imageView itemFrameAtIndex:selectionIndex];
    
    if(!NSIntersectsRect([imageView visibleRect], selectionFrame)) {
        return NSZeroRect;
    }
    
    selectionFrame = [imageView convertRectToBase:selectionFrame];
    selectionFrame.origin = [[imageView window] convertBaseToScreen:selectionFrame.origin];
    return selectionFrame;
}

こちらもずばり、-[IKImageBrowserView itemFrameAtIndex:]が求める物を返してくれます。
ただし、選択されていない場合もありますので、まずは選択アイテムを確認して選択アイテムがなければNSZeroRectを返しています。NSZeroRectを返すと、ズームは発生しません。

さらに、IKImageBrowserViewはIKImageFlowViewと違い、選択アイテムがスクロールアウトされて表示されていない場合がありますのでそれもチェックしています。

あとは、Screen座標に変換して返せばいいです。


最後にControllersListViewController。

ControllersListViewController.mの一部

NSRect centerFitRect(NSImage *image, NSRect targetRect)
{
    NSSize imageSize = [image size];
    CGFloat aspectRatio = imageSize.width / imageSize.height;
    CGFloat newWidth = targetRect.size.height * aspectRatio;
    NSSize fitSize = NSMakeSize(newWidth, targetRect.size.height);
    CGFloat left = (targetRect.size.width - fitSize.width) * 0.5;
    return NSMakeRect(left, targetRect.origin.y, fitSize.width, fitSize.height);
}
- (NSRect)previewPanel:(QLPreviewPanel *)panel sourceFrameOnScreenForPreviewItem:(id <QLPreviewItem>)item
{
    id tableView = [self previewTragetView];
    NSInteger col = [tableView columnWithIdentifier:@"Icon"];
    NSInteger row = [tableView selectedRow];
    NSRect rect = [tableView frameOfCellAtColumn:col row:row];
    NSCell *cell = [tableView preparedCellAtColumn:col row:row];
    NSRect selectionFrame = [cell imageRectForBounds:rect];
    selectionFrame = centerFitRect([cell image], selectionFrame);
    
    if(!NSIntersectsRect([tableView visibleRect], selectionFrame)) {
        return NSZeroRect;
    }
    
    selectionFrame = [tableView convertRectToBase:selectionFrame];
    selectionFrame.origin = [[tableView window] convertBaseToScreen:selectionFrame.origin];
    
    return selectionFrame;
}
- (id)previewPanel:(QLPreviewPanel *)panel transitionImageForPreviewItem:(id <QLPreviewItem>)item contentRect:(NSRect *)contentRect
{
    return [(NSObject *)item valueForKey:@"icon"];
}

ちょっと長いです。
色々実験してみたんですが、NSImageCellから実際に表示されているNSImageのフレームが取れなかったので、NSImageScaleProportionallyDownを仮定して、自力でフレームを計算してます。
もちろん仮定せずに状態に応じたフレームを計算するのが正解ですが今回は気付かなかったことにしています。

-[NSImageCell imageRectForBounds:]がちょっと奇妙な値を返すのでそれに合わせて centerFitRect()関数も奇妙な計算をしてます。

あとは、ControllersIconViewControllerと同じです。


さらに、ControllersListViewControllerのみQLPreviewPanelの最後のDelegateメソッド - (id)previewPanel:transitionImageForPreviewItem:contentRect:を実装しています。
ControllersFlowViewControllerとControllersIconViewControllerではImageKitの力を借りてQuickLookによるプレビュー画像を表示していましたが、ControllersListViewControllerだけは、通常のアイコンか、画像ファイルの中身でした。
そのため、ムービーのようなアイコンとプレビューが全く違うアイテムをプレビューする時、突然画像が切り替わるようになります。それを多少ましにするためにこのメソッドがあります。
返すオブジェクトは実際に表示されている画像です。今回の場合はサンプルのようになります。
さらに、contentRectに表示されている画像のフレームをいれて返すことによってもうちょっとましな感じになります。今回は意味がないのでやってません。
このDelegateメソッドを実装することで、一瞬なので見えないとは思いますが、プレビューと、Delegateメソッドで返した画像がクロスフェードされつつズームイン、ズームアウトされるようになります。


QLPreviewerPanelはこれで終了です。



さて、ホントにネタ切れです。

Controllerとたわむれると題しながらぜんぜん、Controllerと戯れてる感じを出せなかったのはちょっと残念かな?


あとは、モデルをごっそり入れ替えてもControllersAppDelegateを変更するだけで再利用可能!ってのをやろうと思ったんだけど適当なモデルが見つからない。
あ、Spotlightの検索結果をモデルに出来るかも?


てことで、次回はあるかどうか含めて未定で!



Xcode3.2プロジェクト一式
http://www.geocities.jp/svc9826/src/Controllers10.zip