Xamarin.iOS の Core Animation
この記事では、Core Animation フレームワークについて説明し、これがいかにして UIKit でハイ パフォーマンスかつ流れるようなアニメーションを可能にするかと、下位レベルのアニメーション制御のためにこれを直接使用する方法を紹介します。
iOS には、アプリケーション内のビューのアニメーション サポートを提供する Core Animation が含まれています。 表のスクロールや異なるビュー間のスワイプなど、iOS 内の非常にスムーズなアニメーションはすべて、内部的に Core Animation を利用することで実現されています。
Core Animation と Core Graphics フレームワークは連携することで、美しくアニメーション化された 2D グラフィックスを作成できます。 実際、Core Animation は 3D 空間内で 2D グラフィックスを変換し、素晴らしい映画のような体験を生み出すこともできます。 ただし、真の 3D グラフィックスを作成するには、OpenGL ES などを使用する必要があり、ゲームのためには MonoGame などの API に頼る必要がありますが、3D はこの記事の範囲外です。
コア アニメーション
iOS は、Core Animation フレームワークを使用して、たとえば、ビュー間の切り替え、スライディング メニュー、スクロール効果などのアニメーション効果を作成します。 アニメーションを操作するには、以下の 2 つの方法があります。
- UIKit 経由。これにはビューベースのアニメーションと、コントローラー間のアニメーション付き切り替えが含まれます。
- Core Animation 経由。これは直接レイヤー化を行い、きめ細かい制御を可能にします。
UIKit アニメーションの使用
UIKit には、アプリケーションにアニメーションを簡単に追加できるようにするいくつかの機能が用意されています。 これは内部的には Core Animation を使用しますが、それを抽象化するため、利用者はビューとコントローラーの操作を行うだけで済みます。
このセクションでは、以下のような UIKit のアニメーション機能について説明します。
- コントローラー間の切り替え
- ビュー間の切り替え
- ビュー プロパティ アニメーション
ビュー コントローラーの切り替え
UIViewController
には、PresentViewController
メソッドを通したビュー コントローラー間の切り替え用の組み込みサポートが用意されています。 PresentViewController
を使用すると、2 つ目のコントローラーへの切り替えを必要に応じてアニメーション化できます。
たとえば、最初のコントローラー内のボタンに触れると、2 つ目のコントローラーを表示するための PresentViewController
が呼び出されるという 2 つのコントローラーを持つアプリケーションについて考えましょう。 2 つ目のコントローラーを表示するために使用される切り替えアニメーションを制御するには、以下のように ModalTransitionStyle
プロパティを設定するだけで済みます。
SecondViewController vc2 = new SecondViewController {
ModalTransitionStyle = UIModalTransitionStyle.PartialCurl
};
この場合は PartialCurl
アニメーションが使用されますが、以下を含め、いくつかのアニメーションを利用できます。
CoverVertical
– 画面の下部から上にスライドするCrossDissolve
– 古いビューがフェードアウトし、新しいビューがフェードインするFlipHorizontal
- 水平方向の右から左への反転。 無視すると、切り替えは左から右に反転します。
切り替えをアニメーション化するには、以下のように PresentViewController
への 2 番目の引数として true
を渡します。
PresentViewController (vc2, true, null);
次のスクリーンショットは、PartialCurl
のケースで、切り替えがどのように行われるかを示しています。
ビューの切り替え
UIKit は、コントローラー間の切り替えに加えて、1 つのビューを別のビューに入れ替えるビュー間の切り替えのアニメーション化もサポートしています。
たとえば、UIImageView
を使用したコントローラーがあり、その画像をタップすると 2 つ目の UIImageView
を表示する必要があるとしましょう。 画像ビューのスーパービューをアニメーション化して 2 つ目の画像ビューに切り替えるには、以下のように UIView.Transition
を呼び出して、それに toView
と fromView
を渡すだけです。
UIView.Transition (
fromView: view1,
toView: view2,
duration: 2,
options: UIViewAnimationOptions.TransitionFlipFromTop |
UIViewAnimationOptions.CurveEaseInOut,
completion: () => { Console.WriteLine ("transition complete"); });
UIView.Transition
は、アニメーションの実行時間を制御する duration
パラメーターだけでなく、使用するアニメーションやイージング関数などを指定する options
パラメーターも受け取ります。 さらに、アニメーションが完了したときに呼び出される完了ハンドラーを指定することもできます。
次のスクリーンショットは、TransitionFlipFromTop
が使用されているときのアニメーション付き切り替えを示しています。
ビュー プロパティ アニメーション
UIKit は、以下のような UIView
クラス上のさまざまなプロパティのアニメーション化をサポートしています。
- フレーム
- Bounds
- 中央
- Alpha
- 変換
- 色
これらのアニメーションは、静的 UIView.Animate
メソッドに渡される NSAction
デリゲートでプロパティの変更を指定することによって暗黙的に発生します。 たとえば、次のコードは UIImageView
の中心点をアニメーション化します。
pt = imgView.Center;
UIView.Animate (
duration: 2,
delay: 0,
options: UIViewAnimationOptions.CurveEaseInOut |
UIViewAnimationOptions.Autoreverse,
animation: () => {
imgView.Center = new CGPoint (View.Bounds.GetMaxX ()
- imgView.Frame.Width / 2, pt.Y);},
completion: () => {
imgView.Center = pt; }
);
これにより、以下に示すように、画面の上部を前後に移動するアニメーション付きの画像が作成されます。
Transition
メソッドと同様に、Animate
ではイージング関数と共に持続時間を設定できます。 この例では、UIViewAnimationOptions.Autoreverse
オプションも使用しました。これにより、アニメーションが該当値から初期値に戻る動きとなります。 ただし、このコードでは、完了ハンドラーの中で Center
を初期値に設定し直しています。 アニメーションは時間内でプロパティ値を補間していますが、プロパティの実際のモデル値は常に設定された最終値になります。 この例では、その値はスーパービューの右端に近い点です。 Center
を最初の点 (Autoreverse
が設定されているため、これがアニメーションが完了する点になります) に設定しないと、以下に示すように、画像はアニメーションの完了と共に右端にスナップ バックすることになります。
Core Animation の使用
UIView
アニメーションでは多くの機能を利用でき、実装が容易なため、可能であればこれを使用するべきです。 前述のように、UIView アニメーションは Core Animation フレームワークを使用します。 ただし、ビューを使用したアニメーション化が不可能な追加プロパティのアニメーション化や、非線形パスに沿った補間など、UIView
アニメーションでは実行できないものもあります。 そのようなより細かい制御が必要な場合は、Core Animation を直接使用することもできます。
レイヤー
Core Animation を使用する場合、アニメーションは CALayer
型である "レイヤー" を介して行われます。 ビュー階層があるようにレイヤー階層があるという点で、レイヤーは概念的にはビューに似ています。 実際には、レイヤーがビューの基になっており、ビューにはユーザー操作のサポートが追加されています。 どんなビューのレイヤーにも、ビューの Layer
プロパティを介してアクセスできます。 実は、UIView
の Draw
メソッドで使用されるコンテキストは、実際にはレイヤーから作成されています。 内部的には、UIView
の基になっているレイヤーは、そのビュー自体に設定されたデリゲートを持っており、これが Draw
を呼び出しています。 そのため、UIView
に描画する際には、実際はそのレイヤーに描画していることになります。
レイヤー アニメーションは、暗黙的と明示的のどちらかにできます。 暗黙的なアニメーションは宣言型です。 どのレイヤー プロパティを変更するべきかを宣言するだけで、アニメーションは機能します。 一方、明示的なアニメーションは、レイヤーに追加されるアニメーション クラスを介して作成されます。 明示的なアニメーションでは、アニメーションがどのように作成されるかをより細かく制御できます。 以降のセクションでは、暗黙的なアニメーションと明示的なアニメーションについてさらに詳しく見ていきます。
暗黙的なアニメーション
レイヤーのプロパティをアニメーション化する 1 つの方法は、暗黙的なアニメーションを使用することです。 UIView
アニメーションは暗黙的なアニメーションを作成します。 しかし、レイヤーに対して暗黙的なアニメーションを直接作成することもできます。
たとえば、次のコードは、画像からレイヤーの Contents
を設定し、境界線の幅と色を設定し、そのレイヤーをビューのレイヤーのサブレイヤーとして追加します。
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
layer = new CALayer ();
layer.Bounds = new CGRect (0, 0, 50, 50);
layer.Position = new CGPoint (50, 50);
layer.Contents = UIImage.FromFile ("monkey2.png").CGImage;
layer.ContentsGravity = CALayer.GravityResize;
layer.BorderWidth = 1.5f;
layer.BorderColor = UIColor.Green.CGColor;
View.Layer.AddSublayer (layer);
}
レイヤーの暗黙的なアニメーションを追加するには、CATransaction
内のプロパティの変更をラップするだけで済みます。 これにより、以下に示すように BorderWidth
や BorderColor
などのビュー アニメーションではアニメーション化できないプロパティをアニメーション化できます。
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
CATransaction.Begin ();
CATransaction.AnimationDuration = 10;
layer.Position = new CGPoint (50, 400);
layer.BorderWidth = 5.0f;
layer.BorderColor = UIColor.Red.CGColor;
CATransaction.Commit ();
}
このコードでは、レイヤーの Position
のアニメーション化も行います。これは、スーパーレイヤーの座標の左上から測定されたレイヤーのアンカー ポイントの位置です。 レイヤーのアンカー ポイントは、レイヤーの座標系内の正規化された点です。
次の図は、ポジションとアンカー ポイントを示しています。
この例を実行すると、以下のスクリーンショットに示すように、Position
、BorderWidth
、BorderColor
のアニメーションが行われます。
明示的なアニメーション
暗黙的なアニメーションに加えて、Core Animation には CAAnimation
を継承するさまざまなクラスが含まれており、これを使用すると後にレイヤーに明示的に追加されるアニメーションをカプセル化できます。 これにより、アニメーションの開始値の変更、アニメーションのグループ化、非線形パスを実現できるキーフレームの指定など、アニメーションのより細かい制御が可能になります。
次のコードは、先ほど (「暗黙的なアニメーション」セクションにおいて) 示したレイヤーに対して CAKeyframeAnimation
を使用する明示的なアニメーションの例を示しています。
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
// get the initial value to start the animation from
CGPoint fromPt = layer.Position;
/* set the position to coincide with the final animation value
to prevent it from snapping back to the starting position
after the animation completes*/
layer.Position = new CGPoint (200, 300);
// create a path for the animation to follow
CGPath path = new CGPath ();
path.AddLines (new CGPoint[] { fromPt, new CGPoint (50, 300), new CGPoint (200, 50), new CGPoint (200, 300) });
// create a keyframe animation for the position using the path
CAKeyFrameAnimation animPosition = (CAKeyFrameAnimation)CAKeyFrameAnimation.FromKeyPath ("position");
animPosition.Path = path;
animPosition.Duration = 2;
// add the animation to the layer.
/* the "position" key is used to overwrite the implicit animation created
when the layer positino is set above*/
layer.AddAnimation (animPosition, "position");
}
このコードでは、後にキーフレーム アニメーションの定義に使用されるパスを作成することで、レイヤーの Position
を変更します。 レイヤーの Position
がアニメーションの最終的な Position
の値に設定されていることに注意してください。 このアニメーションは実際のモデル値ではなくプレゼンテーション値を変更するだけであるため、これを行わないと、レイヤーはアニメーションの前に突然自身の Position
に戻ることになります。 モデル値をアニメーションの最終的な値に設定することで、レイヤーはアニメーションの最後の位置に留まります。
以下のスクリーンショットは、指定されたパスに沿って動くアニメーション付きの画像を含むレイヤーを示しています。
まとめ
この記事では、Core Animation フレームワークを介して提供されるアニメーション機能について確認しました。 ここでは、Core Animation を調べ、これがどのように UIKit 内のアニメーションを強化し、下位レベルのアニメーション制御のためにどのように直接使用できるかを示しました。