Animating UIView

| | コメント(0) | トラックバック(0)

OSXのLeopardからCoreAnimationというフレームワークが追加された。それによってCoverFlowなんかを実現している。それまでは空間的な管理しか行わなかったGUIシステムにおいて、時間の管理もOSが面倒を見てくれるようになったというのが、CoreAnimationのキモなのではないかと思う。

そして、OSX系の流れをくむiPhoneOSにもCoreAnimationは含まれている。iPhoneOSはOSXの紆余曲折のまだ後の方に出たということもあり、構造的にはモバイルOSにしてはかなりモダンな、OSXの成功も失敗も踏まえた上で設計されていると感じることがある。

さて、iPhoneのCoreAnimationだけど、かなりお手軽に使う方法が用意されている。ダイアログをビヨヨンとだしたり、スライドしてカットインさせたり、はたまた二つのViewをクロスディゾルブさせたり。UIViewControllerのトランジション効果なんかにもプリセットのアニメーションパターンが用意されているのだけど、簡単なお約束でアニメーションさせることが出来るので使わない手はない。

今回デモ用にEscapeButtonというジョークソフトを軽く組んでみた(ほんとに数行しか書いてない)。名前からして出落ちな感もあるけど、実装を見つつご紹介しようと思う。

UIViewをアニメーションさせる

EscapeButtonのソースコードはここ(EscapeButton.zip)にある。

CoreAnimationではCALayerクラスおよび、その派生クラスを扱うのだけど、UIViewにはCoreAnimationを意識せずに使う方法が用意されている。基本のお約束の型はこんな感じ。

[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:0.25];  // アニメーション時間

// ここにアニメーションさせたいUIViewのゴール時の状態を設定する

[UIView commitAnimations];
とりあえず、これだけで指定のUIViewがアニメーションする。たとえばalphaのプロパティ値を0にすればフェードアウトするといった具合に。

ただし、どのプロパティでもアニメーションできるかというと残念ながらそれはできない。ジオメトリ的な事(位置や回転拡大など)、カラー的な事に限られてると考えておいた方が良いだろう(詳しい話はCoreAnimationを見る必要がある)。

さて、EscapeButtonはボタンが押されたらそのボタンが逃げる。いきなりワープするんじゃなくて、するするとアニメーションしつつ上と下を行ったり来たりする。その部分のソースはこんな感じになっている。

- (IBAction)buttonTouchedDown:(id)sender
{
	[UIView beginAnimations:nil context:nil];
	[UIView setAnimationDuration:0.25];

	CGRect frame = _button.frame;

	if (!_side)
		frame.origin.y = 360;
	else
		frame.origin.y = 120;
	_side = !_side;
	_button.frame = frame;

	[UIView commitAnimations];
}
ボタンと、このアクションはInterfaceBuilderにおいてUIControlEventTouchDownイベントで接続している。

やっていることは先ほどの基本の型そのままだ。そして、今いたところとは反対側に移動させる。そのためにボタンのframeを取得してY座標を書き換えてセットしている。これだけなんだけど、ボタンを押すとするすると逃げるという操作が実装できてしまう。

チェーンアニメーション

もう少し複雑なアニメーションの実装をしてみよう。今度はUIViewControllerのviewが表示されるときに回転しながら拡大していき、丁度の位置より行き過ぎて戻るというアニメーションだ。

まずは仕込みだ。初期の段階で縮めておき、さらに90度回転させておく。

- (void)viewWillAppear:(BOOL)animated
{
	CGAffineTransform t = CGAffineTransformMakeScale(0.1, 0.1);
	self.view.transform = CGAffineTransformRotate(t, M_PI_2);
}
UIViewにはtransformというプロパティがあり、そこに2次元変換行列を設定することが出来る。CGAffineTransform〜という関数が用意されていて移動(Translate)、拡大(Scale)、回転(Rotate)が設定できる。回転の角度の指定は度ではなくラジアンだ。まずMakeがついているものでCGAffineTransformを作成し、必要なだけ行列を掛けていく。行列の掛ける順番は意味があるので注意が必要だ(移動してから回転するのと、回転してから移動したのでは結果が異なる)。

そしてViewが表示されたのと同時にアニメーションを開始する。

- (void)viewDidAppear:(BOOL)animated
{
	[UIView beginAnimations:nil context:nil];
	[UIView setAnimationDuration:1];
	[UIView setAnimationDelegate:self];
	[UIView setAnimationDidStopSelector:@selector(_didEndAnimation)];

	CGAffineTransform t = CGAffineTransformMakeScale(1, 1);
	self.view.transform = CGAffineTransformRotate(t, -M_PI_2);

	[UIView commitAnimations];
}
大体は基本の型どおりで、アニメーション終了後に拡大率等倍(=1)、回転がさらに90度過ぎた位置になるようになっている。

基本と違うのはsetAnimationDelegate:とsetAnimationDidStopSelector:が追加されていることで、これは想像の通り、アニメーションが終了したときに指定したターゲットのメソッドが呼ばれるように設定している。

このように終了時にメソッドを呼び出すことによって、例えば禁止していた処理を許可したり、次のアニメーションの再生を開始させたりすることが出来る。今回はさらに0度の位置に戻すようにアニメーションをさせる。

- (void)_didEndAnimation
{
	[UIView beginAnimations:nil context:nil];
	[UIView setAnimationDuration:0.25];

	self.view.transform = CGAffineTransformMakeScale(1, 1);

	[UIView commitAnimations];
}
これは基本通り。単位行列にしている(変形無し)。

これで回転しながら拡大し、ちょっと行き過ぎて戻るというアニメーションが完成した。このView Controllerが表示されるときはこのアニメーションが表示される。プリセットのトランジションにはない効果が簡単に実装できる(今回は重ね合わせに関しては実装をサボっている)。

CoreAnimationを直接使うともっと複雑なことが出来るのだけど、UIViewのラップされたインターフェイスでも、結構動きのあるインターフェイスを実現することが出来る。なかなかお手軽なわりには効果は大きいんじゃないだろうか。

関連記事

トラックバック(0)

このブログ記事を参照しているブログ一覧: Animating UIView

このブログ記事に対するトラックバックURL: http://blog.soprano.jp/mt/mt-tb.cgi/23

コメントする