系统化学习,知其然,知其所以然
动画在用户界面的不同状态之间提供流畅的视觉转换。 在iOS中,动画广泛用于重新定位视图,更改大小,将其从视图层次结构中移除,并将其隐藏起来。 可以使用动画将反馈传达给用户或实现有趣的视觉效果。
在iOS中,创建复杂的动画不需要您编写任何绘图代码。 本章介绍的所有动画技术都使用Core Animation提供的内置支持。 只需触发动画并让Core Animation处理单个帧的渲染。 这使得创建复杂的动画非常容易,只需要几行代码。
一、什么可以动?
UIKit 和 Core Animation 都支持动画,但提供的支持水平不相同。
-
在UIKit中,动画是使用 UIView 对象执行的。 View 动画支持一组更改同时进行;例如同时修改多个view.frame。
以下为View可以做动画的属性:- frame — Use this to animate position and size changes for the view.
- bounds — Use this to animate changes to the size of the view.
- center — Use this to animate the position of the view.
- transform — Use this to rotate or scale the view.
- alpha — Use this to change the transparency of the view.
- backgroundColor — Use this to change the background color of the view.
- contentStretch — Use this to change how the view’s contents stretch.
-
在 Core Animation 中,动画是使用 Layer 对象执行的。在你想要执行更复杂的动画的地方,或者UIView类不支持的动画时,你可以使用Core Animation和View.layer来创建动画。以下为Layer可以做动画的属性:
- The size and position of the layer
- The center point used when performing transformations
- Transformations to the layer or its sublayers in 3D space
- The addition or removal of a layer from the layer hierarchy
- The layer’s Z-order relative to other sibling layers
- The layer’s shadow
- The layer’s border (including whether the layer’s corners are rounded)
- The portion of the layer that stretches during resizing operations
- The layer’s opacity
- The clipping behavior for sublayers that lie outside the layer’s bounds
- The current contents of the layer
- The rasterization behavior of the layer
二、View 属性更改动画
2.1 使用 Begin/Commit 方法进行动画
在iOS 3.2 及以前必须使用
//开始动画
+ (void)beginAnimations:(NSString *)animationID
context:(void *)context;
//提交动画
+ (void)commitAnimations;
//设置开始时间
+ (void)setAnimationStartDate:(NSDate *)startDate;
//设置延迟时间
+ (void)setAnimationDelay:(NSTimeInterval)delay;
//设置动画时间
+ (void)setAnimationDuration:(NSTimeInterval)duration;
//设置动画时使用的曲线
+ (void)setAnimationCurve:(UIViewAnimationCurve)curve;
//设置重复次数
+ (void)setAnimationRepeatCount:(float)repeatCount;
//设置是否自动反转
+ (void)setAnimationRepeatAutoreverses:(BOOL)repeatAutoreverses;
//设置代理
+ (void)setAnimationDelegate:(id)delegate;
//设置动画开始即将执行的方法
+ (void)setAnimationWillStartSelector:(SEL)selector;
//设置动画结束即将执行的方法
+ (void)setAnimationDidStopSelector:(SEL)selector;
//设置是否从当前状态开始动画
+ (void)setAnimationBeginsFromCurrentState:(BOOL)fromCurrentState;
设置动画参数,提交View动画代码。
例如
[UIView beginAnimations:@"ToggleViews" context:nil];
[UIView setAnimationDuration:1.0];
// Make the animatable changes.
firstView.alpha = 0.0;
secondView.alpha = 1.0;
// Commit the changes and perform the animation.
[UIView commitAnimations];
2.2 使用Block进行动画
在iOS 4 以后可以使用Block进行动画
+ (void)animateWithDuration:(NSTimeInterval)duration
animations:(void (^)(void))animations;
+ (void)animateWithDuration:(NSTimeInterval)duration
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion;
+ (void)animateWithDuration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay
options:(UIViewAnimationOptions)options
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion;
由于是类方法,不会绑定到单个view,因此可以一次性修改多个view属性做动画,例如
[UIView animateWithDuration:1.0 animations:^{
firstView.alpha = 0.0;
secondView.alpha = 1.0;
}];
执行此代码时,指定的动画立即在另一个线程上启动,以避免阻塞当前线程或应用程序的主线程。
2.3 动画嵌套
//Block动画块实现
[UIView animateWithDuration:2.0
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
view1.alpha = 0.0;
[UIView animateWithDuration:4.0
delay:0.0
options:UIViewAnimationOptionCurveLinear
animations:^{
view2.alpha = 0.0;
}
completion:nil];
}
completion:nil];
//begin/commit 方法来实现
[UIView beginAnimations:@"animation1" context:nil];
[UIView setAnimationDuration:2.0];
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
btn.alpha = 0;
[UIView beginAnimations:@"animation2" context:nil];
[UIView setAnimationDuration:4.0];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
_slider.alpha = 0;
[UIView commitAnimations];
[UIView commitAnimations];
2.4 动画结束保留到心状态
对于有次数限制的自动反转动画,如果想要动画接受时在新值的状态可以通过repeatCount+非整数来实现该效果。例如 repeatCount+0.5
[view setFrame:CGRectMake(0, 300, 100, 40)];
[UIView animateWithDuration:2
delay:0.0
options:
UIViewAnimationOptionRepeat |
UIViewAnimationOptionAutoreverse
animations:^{
//可以修改次数查看效果,整数值会返回原样
[UIView setAnimationRepeatCount:1.5];
[view setFrame:CGRectMake(0, 300, 300, 40)];
}
completion:nil];
三、View 之间的动画 Creating Animated Transitions Between Views
View 转场动画有以下两种场景:
- 内容较少。更新指定 View 部分内容,如 Subviews
- 内容较多。直接用一个视图替换视图层次结构中的一个视图
3.1 内容较少,更换子视图
通过方法
+ (void)transitionWithView:(UIView *)view
duration:(NSTimeInterval)duration
options:(UIViewAnimationOptions)options
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion;
为view添加过度动画。动画结束时,view还是那个view,但是内容变化了。例如
[UIView transitionWithView:self.view
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
view.alpha = 0.1;
}
completion:nil];
或者
[UIView beginAnimations:@"ToggleSiblings" context:nil];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:YES];
[UIView setAnimationDuration:1.0];
//更改内容
view.alpha = 0.1;
[UIView commitAnimations];
3.2 内容较多,视图替换
当页面变化较大时,需要使用一个新view更换现有view。本质上只是一种使用一些标准转换动画来快速呈现新视图的方法。
在iOS 4和更高版本中,可以使用
+ (void)transitionFromView:(UIView *)fromView
toView:(UIView *)toView
duration:(NSTimeInterval)duration
options:(UIViewAnimationOptions)options
completion:(void (^)(BOOL finished))completion;
方法在两个视图之间切换。这个方法实际上是从你的视图层次结构中删除第一个视图,然后插入另一个视图,所以你应该确保你有第一个视图的引用,如果你想保留它。如果要隐藏视图而不是从视图层次结构中删除视图,请将UIViewAnimationOptionShowHideTransitionViews
项作为选项之一传递。
在这个例子中,视图控制器的根视图总是显示两个子视图(primaryView或secondaryView)之一。每个视图呈现相同的内容,但以不同的方式。视图控制器使用displayPrimary成员变量(布尔值)来跟踪在任何给定时间显示哪个视图。翻转方向根据正在显示的视图而改变。
- (void)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;
}
}];
}
四、顺序执行多个动画 Linking Multiple Animations Together
UIView动画接口提供了链接单独的动画块的支持,以便它们按顺序执行而不是同时执行。 链接动画块的过程取决于使用基于 Block 的动画方法还是使用 begin/commit 方法。有2种实现思路:
-
思路1:从代码执行逻辑上看是一个接受后执行另外一个
- 对于基于 Block 的动画,请使用带有completion 回调的方法执行下一个动画
。animateWithDuration所支持的完成处理程序:动画:completion:和animateWithDuration:delay:options:animations:completion:执行任何后续动画的方法。 - 对于 begin/commit ,请设置delegate 和 did-stop selector。
- 对于基于 Block 的动画,请使用带有completion 回调的方法执行下一个动画
+ (void)animateWithDuration:(NSTimeInterval)duration
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion;
+ (void)animateWithDuration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay
options:(UIViewAnimationOptions)options
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion;
- 思路2:通过设置不同的时间延迟
将动画链接在一起的替代方法是使用具有不同延迟因子的嵌套动画,以便在不同时间启动动画。
五、View 动画和 Layer 动画 混合执行
APP可以根据需要自由地混合基于视图和基于图层的动画代码,但配置动画参数的过程取决于谁拥有图层。
更改View.layer与更改view本身相同,并且应用于图层属性的任何动画都当以前基于视图的动画块的动画参数为准。
但是自定义图层不是如此。自定义图层对象会忽略基于视图的动画块参数,而是使用默认的 Core Animation 参数。
如果要为所创建的图层自定义动画参数,则必须直接使用 Core Animation。通常,Layer 使用 Core Animation 创建一个 CABasicAnimation 对象或 CAAnimation 的其他具体子类。然后,将该动画添加到相应的图层。可以在视图的动画块内部或外部使用动画。例如,
[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;
}];
}];
该示例显示了一个动画,它同时修改一个视图和一个自定义图层。此示例中的视图在其bounds的中心包含一个自定义CALayer对象。
- 首先View 逆时针旋转,
- 然后layer 同时顺时针旋转,
- 最后View 逆时针旋转360度。
由于旋转方向相反,图层相对于屏幕保持其原始方向,并且不旋转效果。然而,最后view在动画结束后旋转360度,并返回到原来的方向。这个例子主要是为了演示如何混合视图和图层动画。
如果您的视图和基于图层的动画之间需要精确的时间,建议您使用Core Animation创建所有的动画。