ページ

2009年1月21日水曜日

内容によってウィンドウのサイズを変える (2) NSViewAnimation

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

タブ切り替えの場合、新しいタブの内容が表示された状態でウィンドウのリサイズアニメーションが行われる。これは実際に動きを見てもらうとわかるのだが、大きいサイズから小さいサイズへアニメーションする時は下に余白が出て、逆の場合はアニメーションの過程で見えなかった領域のボタンなどのコントロールが現れてきて、あまり見た目は良くない。

Mail.app の場合はリサイズアニメーションが開始されたらタブの内容を一旦消して、アニメーションが完了した時点で内容を表示するようにしている。なるほどちゃんと作り込んである。こういう視覚効果にするにはどうしたら良いか?

NSWindowに delegateメソッド windowDidResize: が用意されているが、リサイズアニメーションの過程で何度か呼出されてしまうため、リサイズの完了を知ることができない。通常はリサイズ完了を知らなくても問題ないので手軽に使える NSWindow#setFrame:display:animate: で十分なのだが、今回の用途ではちょっと役不足のようだ。

さてどうするか。そういえば昨年フェードアウトの検証で NSViewAnimation を使ったが、あれがリサイズアニメーションをサポートしていた。このクラスはアニメーションの開始・終了の通知を delegateで受け取ることができる。

(参考)Windowアニメーション(その2)

これを使って前回のサンプルを改造してみよう。

AppController.m

- (void)update
{
 :
// 前回の実装
// [_window setFrame:window_frame display:YES animate:YES];

// NSViewAnimationを使った実装
NSMutableDictionary* dict = [NSMutableDictionary dictionary];
[dict setObject:_window forKey:NSViewAnimationTargetKey];
[dict setObject:[NSValue valueWithRect:[_window frame]]
forKey:NSViewAnimationStartFrameKey];
[dict setObject:[NSValue valueWithRect:window_frame]
forKey:NSViewAnimationEndFrameKey];
NSViewAnimation* anim = [[NSViewAnimation alloc]
initWithViewAnimations:[NSArray arrayWithObject:dict]];
[anim setDuration:0.25];
[anim setDelegate:self];
[anim startAnimation];
[anim release];
}

コード量は増えたがそれでも簡単にアニメーションを実行できる。NSMutableDictionaryに開始範囲(NSViewAnimationStartFrameKey)と終了範囲(NSViewAnimationEndFrameKey)を指定し、スタートさせるだけ。delegateで開始と終了通知を受け取れる。

- (BOOL)animationShouldStart:(NSAnimation *)animation
{
[[[_tab_view selectedTabViewItem] view] setHidden:YES];
return YES;
}
- (void)animationDidEnd:(NSAnimation *)animation
{
[[[_tab_view selectedTabViewItem] view] setHidden:NO];
}

開始と終了で新しく選択されたタブのビューの表示制御を行ってやる。


さて実行してみよう。


タブを切り替えるとウィンドウのリサイズアニメーションが始まる。この時、選択したタブの内容(ボタンB)は表示されない。


アニメーションが完了するとタブの内容が表示される。




検証ができたので次は SimpleCapのプリファレンスへ組み込む。

サンプル:ElasticWindow-2.zip