苹果官方文档View Programming Guide for iOS
五、动画
动画在用户界面的不同状态之间提供流畅的视觉转换。 在iOS中,动画广泛用于重新定位view,更改大小,将其从view层次结构中移除,并将其隐藏起来。 您可以使用动画将反馈传达给用户或实现有趣的视觉效果。
在iOS中,创建复杂的动画不需要您编写任何绘图代码。 本章介绍的所有动画技术都使用Core Animation提供的内置支持。 您只需触发动画并让Core Animation处理单个帧的渲染。 这使得创建复杂的动画非常容易,只需要几行代码。
哪些可以有动画?
UIKit和Core Animation都支持动画,但每种技术提供的支持水平都不相同。 在UIKit中,动画是使用UIView对象执行的。 view支持一组涵盖许多常见任务的基本动画。 例如,您可以对view的属性进行动画更改,或使用过渡动画将一组view替换为另一组view
Property | Changes you can make |
---|---|
frame |
修改这个属性来修改View的位置和大小 (如果View的 transform 没有包含identity transform, 那么取而代之,修改 bounds or center .) |
bounds |
修改这个属性来修改 view’s size. |
center |
修改这个属性来修改相对于父View坐标系统的中心点 |
transform |
修改这个属性来修改相对于其中心点的移动缩放旋转. 这是2D层面的 (如果要3D层面需要使用 Core Animation.) |
alpha |
修改这个属性来修改 view的透明度. |
backgroundColor |
修改这个属性来修改View的背景颜色 |
contentStretch |
修改这个属性来使内容更好地适应填充 |
动画view转换是一种让您对view hierarchy进行更改的方式,而不是view controller提供的view hierarchy。虽然您应该使用view controller来管理简洁的view hierarchy,但有时您可能需要替换全部或部分view hierarchy。在这些情况下,您可以使用基于view的转换来动画添加和删除view。 在你想要执行更复杂的动画的时候,或者UIView类不支持的动画中,你可以使用Core Animation和view的底层layer来创建动画。由于view和layer对象错综复杂地链接在一起,因此对view layer的更改会影响view本身。使用核心动画,您可以为您的view的layer设置以下类型的更改:
- layer的大小和位置
- 执行转换时使用的中心点
- 转换到三维空间中的layer或其sublayer
- 从layer分层结构中添加或删除layer
- 相对于其他兄弟layer的Z层顺序
- layer的shadow
- layer的border(包括layer的边角是否圆整)
- 在调整大小操作期间延伸的layer部分
- layer的不透明度
- 位于layer边界之外的子layer的剪切行为
- layer的当前内容
- layer的栅格化行为
注意,如果你的View用了一个自定义的layer对象,也就是这个对象没有与View相关联,你必须用Core Animation来修改它
用Block动画
有几种基于block的方法为动画block提供不同级别的配置。 这些方法是:
animateWithDuration:animations:
animateWithDuration:animations:completion:
animateWithDuration:delay:options:animations:completion:
由于这些是类方法,因此您使用它们创建的动画块不会绑定到单个view。 因此,您可以使用这些方法创建一个包含对多个view进行更改的动画
[UIView animateWithDuration:1.0 animations:^{
firstView.alpha = 0.0;
secondView.alpha = 1.0;
}];
前一个例子中的动画只运行一次,使用一个ease-in,ease-out的动画曲线。 如果要更改默认的动画参数,则必须使用animateWithDuration:delay:options:animations:completion:
方法来执行动画。 该方法可以让您自定义以下动画参数:
- 开始动画之前使用的延迟
- 在动画中使用的时间曲线的类型
- 动画应该重复的次数
- 当动画到达最后时,动画是否会自动反转
- 触摸事件是否在动画进行过程中传递到view
- 动画是否应该中断任何正在进行的动画,或者在开始之前等到动画完成
- (IBAction)showHideView:(id)sender
{
// Fade out the view right away
[UIView animateWithDuration:1.0
delay: 0.0
options: UIViewAnimationOptionCurveEaseIn
animations:^{
thirdView.alpha = 0.0;
}
completion:^(BOOL finished){
// Wait one second and then fade in the view
[UIView animateWithDuration:1.0
delay: 1.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
thirdView.alpha = 1.0;
}
completion:nil];
}];
}
重要提示:当涉及该属性的动画已在进行中时,更改属性的值不会停止当前动画。 而是当前的动画继续,新值会有动画。
如果您的应用程序在iOS 3.2及更早版本中运行,则必须使用UIView的beginAnimations:context:
和commitAnimations
类方法来定义您的动画块。iOS4之后则应该用block动画
动画block嵌套
您可以通过嵌套其他动画blcok来为动画block的某些部分分配不同的时序和配置选项。 顾名思义,嵌套动画block是在现有动画block内创建的新动画block。 嵌套动画与任何父动画同时启动,但运行(大部分)与他们自己的配置选项。 默认情况下,嵌套动画会继承父级的持续时间和动画曲线,但即使这些选项可以根据需要被覆盖
[UIView animateWithDuration:1.0
delay: 1.0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
aView.alpha = 0.0;
// Create a nested animation that has a different
// duration, timing curve, and configuration.
[UIView animateWithDuration:0.2
delay:0.0
options: UIViewAnimationOptionOverrideInheritedCurve |
UIViewAnimationOptionCurveLinear |
UIViewAnimationOptionOverrideInheritedDuration |
UIViewAnimationOptionRepeat |
UIViewAnimationOptionAutoreverse
animations:^{
[UIView setAnimationRepeatCount:2.5];
anotherView.alpha = 0.0;
}
completion:nil];
}
completion:nil];
在这种情况下,两个view正在被淡化为完全透明,但是另一个view对象的透明度在最终隐藏之前来回地多次改变。 在嵌套动画块中使用的UIViewAnimationOptionOverrideInheritedCurve和UIViewAnimationOptionOverrideInheritedDuration键允许为第二个动画修改第一个动画的曲线和持续时间值。 如果这些键不存在,则将使用外部动画块的持续时间和曲线。
在View之间创建动画转换
view转换可帮助您隐藏与在view层次结构中添加,删除,隐藏或显示view相关的突然更改。 您使用view transitions来实现以下类型的更改:
- 更改现有view的可见subview。 当您想对现有view进行相对较小的更改时,通常会选择此选项。
- 用不同的view替换view层次结构中的一个view。 如果要替换跨越全部或大部分屏幕的view层次结构,通常选择此选项。
重要提示:view转换不应与view controller启动的转换相混淆,例如present view controller的呈现或将新view controller推到navigation堆栈上。 view转换仅影响view层次,而view - controller转换也改变活动view controller。 因此,对于view转换,如果你在初始化转换时保持活动,那么VC在转换结束时也是活动的
修改View的subviews
更改view的subview允许您对view进行适度更改。例如,您可以添加或删除subview以在两种不同状态之间切换superview。在动画完成时,显示相同的view,但其内容现在不同。
在iOS 4和更高版本中,使用transitionWithView:duration:options:animations:completion:
方法为view启动过渡动画。在传递给此方法的动画块中,通常动画的唯一更改是与显示,隐藏,添加或删除子view相关的更改。将动画限制为该集合允许view创建view之前和之后版本的快照图像,并且在两个图像之间创建动画,这更高效。但是,如果您需要动画其他更改,则可以在调用方法时包含UIViewAnimationOptionAllowAnimatedContent
选项。包含该选项可防止view创建快照,并直接动画化所有更改。
- (IBAction)displayNewPage:(id)sender
{
[UIView transitionWithView:self.view
duration:1.0
options:UIViewAnimationOptionTransitionCurlUp
animations:^{
currentTextView.hidden = YES;
swapTextView.hidden = NO;
}
completion:^(BOOL finished){
// Save the old text and then swap the views.
[self saveNotes:temp];
UIView* temp = currentTextView;
currentTextView = swapTextView;
swapTextView = temp;
}];
}
替换一个View
- (IBAction)toggleMainViews:(id)sender {
[UIView transitionFromView:(displayingPrimary ? primaryView : secondaryView)
toView:(displayingPrimary ? secondaryView : primaryView)
duration:1.0
options:(displayingPrimary ? UIViewAnimationOptionTransitionFlipFromRight :
UIViewAnimationOptionTransitionFlipFromLeft)
completion:^(BOOL finished) {
if (finished) {
displayingPrimary = !displayingPrimary;
}
}];
}
View和layer动画一起
应用程序可以根据需要自由混合基于view和基于layer的动画代码,但配置动画参数的过程取决于谁拥有layer。 更改view拥有的层与更改view本身相同,并且应用于layer属性的任何动画都尊重当前基于view的动画块的动画参数。 你自己创建的layer也是如此。 自定义layer对象会忽略基于view的动画块参数,而是使用默认的“核心动画”参数。
如果要为所创建的layer自定义动画参数,则必须直接使用Core Animation。 通常,使用Core Animation动画化layer包括创建一个CABasicAnimation对象或CAAnimation的其他具体子类。 然后,您将该动画添加到相应的layer。 您可以从基于view的动画块内部或外部应用动画。
[UIView animateWithDuration:1.0
delay:0.0
options: UIViewAnimationOptionCurveLinear
animations:^{
// Animate the first half of the view rotation.
CGAffineTransform xform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(-180));
backingView.transform = xform;
// Rotate the embedded CALayer in the opposite direction.
CABasicAnimation* layerAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
layerAnimation.duration = 2.0;
layerAnimation.beginTime = 0; //CACurrentMediaTime() + 1;
layerAnimation.valueFunction = [CAValueFunction functionWithName:kCAValueFunctionRotateZ];
layerAnimation.timingFunction = [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionLinear];
layerAnimation.fromValue = [NSNumber numberWithFloat:0.0];
layerAnimation.toValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(360.0)];
layerAnimation.byValue = [NSNumber numberWithFloat:DEGREES_TO_RADIANS(180.0)];
[manLayer addAnimation:layerAnimation forKey:@"layerAnimation"];
}
completion:^(BOOL finished){
// Now do the second half of the view rotation.
[UIView animateWithDuration:1.0
delay: 0.0
options: UIViewAnimationOptionCurveLinear
animations:^{
CGAffineTransform xform = CGAffineTransformMakeRotation(DEGREES_TO_RADIANS(-359));
backingView.transform = xform;
}
completion:^(BOOL finished){
backingView.transform = CGAffineTransformIdentity;
}];
}];