Xamarin.iOS 中的核心动画
本文介绍 Core Animation 框架,其中介绍了如何在 UIKit 中实现高性能、流畅的动画,以及如何直接将其用于较低级别的动画控件。
iOS 提供核心动画,用于为应用程序中的视图提供动画支持。 iOS 中所有超流畅的动画(例如,滚动表格和在不同视图之间轻扫)的表现都非常出色,因为它们在内部依赖于核心动画。
核心动画框架和核心图形框架可协同工作,创建漂亮的动画 2D 图形。 事实上,核心动画甚至可以在 3D 空间中变换 2D 图形,创造出令人惊叹的电影体验。 但是,若要创建真正的 3D 图形,需要使用 OpenGL ES 这样的的内容,对于游戏则使用 MonoGame 这样的 API,尽管 3D 不在本文介绍范围内。
核心动画
iOS 使用核心动画框架来创建动画效果,例如在视图之间过渡、滑动菜单和滚动效果等等。 有两种处理动画的方法:
使用 UIKit 动画
UIKit 提供了几个功能,使得可轻松地向应用程序添加动画。 尽管它在内部使用核心动画,但它会将其抽象化,所以你只使用视图和控制器。
本部分讨论 UIKit 动画功能,包括:
- 控制器之间的过渡
- 视图之间的过渡
- 查看属性动画
视图控制器转换
UIViewController
对通过 PresentViewController
方法在视图控制器之间进行过渡提供内置支持。 使用 PresentViewController
时,可以选择性地将到第二个控制器的过渡动画化。
例如,假设一个应用程序有两个控制器,其中触摸第一个控制器中的按钮会调用 PresentViewController
来显示第二个控制器。 若要控制用于显示第二个控制器的过渡动画,只需设置其 ModalTransitionStyle
属性即可,如下所示:
SecondViewController vc2 = new SecondViewController {
ModalTransitionStyle = UIModalTransitionStyle.PartialCurl
};
在本例中,使用了 PartialCurl
动画,尽管有其他几个动画可用,其中包括:
CoverVertical
- 从屏幕底部向上滑动CrossDissolve
- 旧视图淡出,新视图淡入FlipHorizontal
- 从右到左水平翻转。 关闭时,过渡从右到左翻转。
若要对过渡进行动画处理,请将 true
作为第二个参数传递给 PresentViewController
:
PresentViewController (vc2, true, null);
以下屏幕截图显示了在 PartialCurl
的情况下过渡的呈现效果:
查看过渡
除了控制器之间的过渡外,UIKit 还支持在视图之间进行动画过渡,将一个视图交换成另一个视图。
例如,假设你有一个带有 UIImageView
的控制器,其中点击图像会显示第二个 UIImageView
。 若要对图像视图的超级视图进行动画处理以过渡到第二个图像视图,只需调用 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
类上免费创建各种属性的动画,包括:
- Frame
- 边界
- 中心
- Alpha
- 转换
- Color
这些动画通过在传递给静态 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
的情况下动画完成的位置),图像将在动画完成后弹回到右侧,如下图所示:
使用核心动画
UIView
动画允许大量功能,由于易于实现,因此应尽可能地使用这些动画。 如前所述,UIView 动画使用核心动画框架。 但是,某些内容是无法用 UIView
动画完成的,例如,对无法使用视图进行动画的其他属性创建动画,或者沿非线性路径内插。 在需要精细控制的情况下,也可以直接使用核心动画。
图层
使用核心动画时,动画通过图层发生,这些图层的类型是 CALayer
。 图层在概念上类似于视图,因为有一个图层层次结构,就像有视图层次结构一样。 实际上,图层支持视图,支持添加视图来进行用户交互。 可以通过视图的 Layer
属性访问任何视图的图层。 事实上,在 UIView
的 Draw
方法中使用的上下文实际上是从图层创建的。 在内部,支持 UIView
的图层的委托设置为视图本身,也就是调用 Draw
。 因此,在绘制到 UIView
图层时,你实际上正在绘制到它的图层。
图层动画可以是隐式的,也可以是显式的。 隐式动画是声明性的。 只需声明应更改哪些图层属性,动画就可以工作了。 另一方面,显式动画是通过添加到图层中的动画类创建的。 显式动画允许对动画的创建方式进行额外的控制。 以下部分将更深入地探讨隐式动画和显式动画。
隐式动画
要将图层属性动画化,一种方法是通过隐式动画。 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
的动画效果如下面的屏幕截图所示:
显式动画
除了隐式动画外,核心动画还包括各种从 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
,因为动画只更改了表示值,没有更改实际的模型值。 通过将模型值设置为动画的最终值,图层将在动画结束时保留在原位。
以下屏幕截图显示了包含通过指定路径动画的图像的图层:
总结
本文介绍了通过核心动画框架提供的动画功能。 我们了解了核心动画,展示它在 UIKit 中如何为动画提供支持,以及如何直接用于较低级别的动画控制。