次の方法で共有


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 のケースで、切り替えがどのように行われるかを示しています。

このスクリーンショットは、PartialCurl の遷移を示しています

ビューの切り替え

UIKit は、コントローラー間の切り替えに加えて、1 つのビューを別のビューに入れ替えるビュー間の切り替えのアニメーション化もサポートしています。

たとえば、UIImageView を使用したコントローラーがあり、その画像をタップすると 2 つ目の UIImageView を表示する必要があるとしましょう。 画像ビューのスーパービューをアニメーション化して 2 つ目の画像ビューに切り替えるには、以下のように UIView.Transition を呼び出して、それに toViewfromView を渡すだけです。

UIView.Transition (
  fromView: view1,
  toView: view2,
  duration: 2,
  options: UIViewAnimationOptions.TransitionFlipFromTop |
    UIViewAnimationOptions.CurveEaseInOut,
  completion: () => { Console.WriteLine ("transition complete"); });

UIView.Transition は、アニメーションの実行時間を制御する duration パラメーターだけでなく、使用するアニメーションやイージング関数などを指定する options パラメーターも受け取ります。 さらに、アニメーションが完了したときに呼び出される完了ハンドラーを指定することもできます。

次のスクリーンショットは、TransitionFlipFromTop が使用されているときのアニメーション付き切り替えを示しています。

このスクリーンショットは、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 が設定されているため、これがアニメーションが完了する点になります) に設定しないと、以下に示すように、画像はアニメーションの完了と共に右端にスナップ バックすることになります。

Center を最初の点に設定しないと、画像はアニメーションの完了後に右側にスナップバックします

Core Animation の使用

UIView アニメーションでは多くの機能を利用でき、実装が容易なため、可能であればこれを使用するべきです。 前述のように、UIView アニメーションは Core Animation フレームワークを使用します。 ただし、ビューを使用したアニメーション化が不可能な追加プロパティのアニメーション化や、非線形パスに沿った補間など、UIView アニメーションでは実行できないものもあります。 そのようなより細かい制御が必要な場合は、Core Animation を直接使用することもできます。

レイヤー

Core Animation を使用する場合、アニメーションは CALayer 型である "レイヤー" を介して行われます。 ビュー階層があるようにレイヤー階層があるという点で、レイヤーは概念的にはビューに似ています。 実際には、レイヤーがビューの基になっており、ビューにはユーザー操作のサポートが追加されています。 どんなビューのレイヤーにも、ビューの Layer プロパティを介してアクセスできます。 実は、UIViewDraw メソッドで使用されるコンテキストは、実際にはレイヤーから作成されています。 内部的には、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 内のプロパティの変更をラップするだけで済みます。 これにより、以下に示すように BorderWidthBorderColor などのビュー アニメーションではアニメーション化できないプロパティをアニメーション化できます。

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 のアニメーション化も行います。これは、スーパーレイヤーの座標の左上から測定されたレイヤーのアンカー ポイントの位置です。 レイヤーのアンカー ポイントは、レイヤーの座標系内の正規化された点です。

次の図は、ポジションとアンカー ポイントを示しています。

この図は、位置とアンカー ポイントを示しています

この例を実行すると、以下のスクリーンショットに示すように、PositionBorderWidthBorderColor のアニメーションが行われます。

この例を実行すると、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 内のアニメーションを強化し、下位レベルのアニメーション制御のためにどのように直接使用できるかを示しました。