UIView动画和Core Animation框架,事实上IOS动画的核心技术是Core Animation(缩写为CA),而UIView动画本质上也是Core Animation框架实现的,只不过进行了封装和优化。此外,本章还介绍IOS7新动画相关技术UIKit力学(UIKit Dynamics)和运动效果(Motion Effects).
4.1 视图动画
每个视图都关联到一个图层(CALayer)对象,视图主要用来处理事件,图层用来处理动画,视图上所有动画、绘制和可视效果都直接或间接地由层处理。
视图由一系列支持动画的属性,包括frame、bounds、center、alpha和transform等。此外,还有一些属性如下:动画延迟事件、动画曲线(淡入/淡出、淡入、淡出和线性等)、动画过渡、重复次数和自动反转等属性。
4.1.1 动画块
动画效果的对应属性设定是放在动画块中的,在IOS4.0之前我们是通过UIView下面方法设置动画块的。
[UIView beginAnimations:@"animations" context:nil];
...
[UIView commitAnimations];
在IOS4.0之后又推出了采用代码块(Block)的方法,这些UIView方法如下:
- +(void)animateWithDuration:delay:options:animations:completion:
- +(void)animateWithDuration: animations:completion:
- +(void)animateWithDuration:animations:
这些方法都是UIView的类方法,需要实例化就可以直接调用,其中animationWithDuration后面的参数是动画持续的时间,animations:后面是一个代码块,用来设置动画的属性。completion:也上一个代码块,是在动画结束时候调用执行的。delay:后面是动画延迟的时间,如果为0则动画马上执行
下面通过一个实例介绍一下动画块的使用,以及动画处理的过程。这个实力如图所示,在屏幕上有一个球形图片和一个Tap Me的按钮,当用户单击Tap Me按钮时,球体向下移动100点,持续时间是1.5秒。当再次单击Tap Me按钮的时候,球体向上移动100个点,就这样反复运动。
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
#import "ViewController.h"
@interface ViewController ()
{
int flag ;
}
@property (weak, nonatomic) IBOutlet UIImageView *ball;
- (IBAction)click:(id)sender;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
flag = 1;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)click:(id)sender {
/*
[UIView beginAnimations:@"animations" context:nil];
[UIView setAnimationDuration:1.5];
CGRect frame = self.ball.frame;
frame.origin.y += 100 * flag;
flag *= -1; //取反
self.ball.frame = frame;
[UIView commitAnimations];
*/
[UIView animateWithDuration:1.5 animations:^{
CGRect frame = self.ball.frame;
frame.origin.y += 100 * flag;
flag *= -1; //取反
self.ball.frame = frame;
}];
}
@end
4.1.2 动画生命周期事件
有的时候,需要捕获动画开始与结束事件,iOS4.0之前采用的beginAnimation:和commitAnimations方法时我们可以设置动画委托对象,然后再通过下面方法设置动画开始后的回调方法。
+(void)setAnimationWillStartSelector:(SEL)selector
下面方法时设置动画结束后的回调方法
+(void)setAnimationDidStopSelector:(SEL)selector
而在iOS4.0之后,如果采用下面方法实现动画功能时,可以completion:实现动画结束的处理。但这些方法并没有动画开始的代码块
+animateWithDuration:animations:completion:
+animateWithDuration:delay:options:animations:completion:
下面通过一个实例介绍它们的用法。在上个实例上进行了修改,当单击Tap Me按钮时候按钮小时,动画开始。当动画结束的时候,Tap Me按钮又显示出来了。
iOS4.0之前的代码
- (IBAction)click:(id)sender {
[self.button setAlpha:0.0];
[UIView beginAnimations:@"animations" context:nil];
[UIView setAnimationDuration:1.5];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(viewAnimationDone)];
CGRect frame = self.ball.frame;
frame.origin.y += 100 * flag;
flag *= -1; //取反
self.ball.frame = frame;
[UIView commitAnimations];
}
- (void)viewAnimationDone {
[UIView beginAnimations:@"animationDone" context:nil];
[self.button setAlpha:1.0];
[UIView commitAnimations];
}
iOS4.0之后的代码
- (IBAction)click:(id)sender {
[self.button setAlpha:0.0];
[UIView animateWithDuration:1.5 animations:^{
CGRect frame = self.ball.frame;
frame.origin.y += 100 * flag;
flag *= -1; //取反
self.ball.frame = frame;
} completion:^(BOOL finished) {
NSLog(@"动画结束了。");
[self viewAnimationDone];
}];
}
- (void)viewAnimationDone {
[UIView animateWithDuration:1. animations:^{
[self.button setAlpha:1.0];
}];
}
4.1.3 过渡动画
前文中提及的动画还有很多属性,例如动画曲线、过渡(界面跳转)动画、重复次数和自动反转等重要的属性,其中有一些事适用于过渡动画(Animation Transition)技术的,如动画曲线属性等。下面通过一个实例介绍过渡动画的属性使用。
- (IBAction)doUIViewAnimation:(id)sender {
[UIView beginAnimations:@"animationID" context:nil];
[UIView setAnimationDuration:3.0f];
// 设置动画曲线 动画曲线是设置动画过渡执行速度的变化情况
[UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
// 不重复动画
[UIView setAnimationRepeatAutoreverses:NO];
UIButton *theButton = (UIButton *)sender;
switch (theButton.tag) {
case 1:
// 第一个参数定义动画过渡类型 第二个参数事当前视图对象 第三个参数事是否适用缓存区
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.view cache:YES];
break;
case 2:
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:self.view cache:YES];
break;
case 3:
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:YES];
break;
case 4:
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:self.view cache:YES];
break;
default:
break;
}
[UIView commitAnimations];
}
doUIViewAnimation:方法实现动画的处理,其中参数UIVIewAnimationCurveEaseOut是在枚举UIViewAnimationCurve中定义的常量,相关的常量还有:
UIViewAnimationCurveEaseInOut, //缓入缓出 即开始和结束时减速
UIViewAnimationCurveEaseIn, //缓入,开始时减速
UIViewAnimationCurveEaseOut, //缓出,结束时减速
UIViewAnimationCurveLinear //线性,即匀速运动
setAnimationTransition:forView:cache:方法的第一个参数定义动画过渡类型,第二个参数是当前视图对象,第三个参数是是否使用缓冲区。动画过渡类型相关的常量有:
- UIViewAnimationTransitionNone,不设置过渡动画
- UIViewAnimationTransitionFlipFromLeft,设置从左往右翻转
- UIViewAnimationTransitionFlipFromRight,设置从右往左翻转
- UIViewAnimationTransitionCurlUp,设置向上翻页
- UIViewAnimationTransitionCurlDown,设置向下翻页
在iOS4.0之后,专门为动画过渡添加了两个方法:
- +transitionWithView:duration:options:animations:completion:在指定的视图容器内创建动画过渡
- +transitionFormView:toView:duration:option:completion:在指定的两个视图之间创建动画过渡
在上面的两个方法中options参数是设置动画如何执行的常量(UIViewAnimationOptions),UIViewAnimationOptions中有很多常量,其中有关于动画曲线的常量有:
- UIViewAnimationOptionCurveEaseInOut
- UIViewAnimationOptionCurveEaseIn
- UIViewAnimationOptionCurveEaseOut
- UIViewAnimationOptionCurveLinear
有关动画过渡的常量有
- UIViewAnimationOptionTransitionNone
- UIViewAnimationOptionTransitionFlipFromLeft
- UIViewAnimationOptionTransitionFlipFromRight
- UIViewAnimationOptionTransitionCurlUp
- UIViewAnimationOptionTransitionCurlDown
- UIViewAnimationOptionTransitionCrossDissolve
- UIViewAnimationOptionTransitionFlipFromTop
- UIViewAnimationOptionTransitionFlipFromBottom
主要代码有:
- (IBAction)doUIViewAnimation:(id)sender {
UIButton *theButton = (UIButton *)sender;
switch (theButton.tag) {
case 1:
[UIView transitionWithView:self.view duration:3.0f
options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionTransitionFlipFromLeft
animations:NULL completion:NULL];
break;
case 2:
[UIView transitionWithView:self.view duration:3.0f
options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionTransitionFlipFromRight
animations:NULL completion:NULL];
break;
case 3:
[UIView transitionWithView:self.view duration:3.0f
options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionTransitionCurlUp
animations:NULL completion:NULL];
break;
case 4:
[UIView transitionWithView:self.view duration:3.0f
options:UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionTransitionCurlDown
animations:NULL completion:NULL];
break;
}
}
如果options参数很多在不发生冲突的情况下可以按位或运算,options参数写法如下:
UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionTransitionCurlDown
4.2 iOS7自定义视图过渡动画
iOS7允许用户自定义视图过渡动画
视图过渡即视图之间的跳转有两种情况:树形结构导航和模态导航,树形结构导航是通过UINavigationController控制视图堆栈实现视图过渡,模态导航是通过UIViewController控制视图实现的。由于这两种不同的过渡,因此在iOS7中自定义视图过渡也有两种不同的场景。
4.2.1 树形结构导航自定义过渡动画
在iOS7中树形结构导航自定义过渡动画涉及实现两个协议:UIViewControllerAnimatedTransitioning和UINavigationControllerDelegate。我们想要自己定义动画对象,这个对象要求实现UIViewControllerAnimatedTransitionging协议,UIView
ControllerAnimatedTransitioning主要定义了两个方法:
animateTransition:(id<UIViewControllerContextTransitioning>)tc,执行自定义动画
transitionDuration:(id<UIViewControllerContextTransitioning>),设置动画执行的时间
这两个方法中的UIViewControllerContextTransitioning类封装过渡动画的上下文对象,我们可以通过这个上下文对象获得过渡前后的视图控制器,相关代码如下:
UIViewController *fromVC = [transitionContext ViewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext ViewControllerForKey:UITransitionContextToViewControllerKey];
下面的代码是实现UIViewControllerAnimatedTransitioning协议的,我们自定义过渡动画类SlideTransitionAnimator
#import <Foundation/Foundation.h>
// 声明实现UIViewControllerAnimatedTransitioning协议
@interface SliderTransitionAnimator : NSObject <UIViewControllerAnimatedTransitioning>
//标识目标视图是否已经呈现
@property (nonatomic) BOOL isPresenting;
@end
#import "SliderTransitionAnimator.h"
@implementation SliderTransitionAnimator
- (NSTimeInterval) transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return 0.5;
}
- (void)animateTransition:(id<UIViewControllerTransitioning>)transitionContext {
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerkey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerkey];
...
if (self.isPresenting) {
[transitionContext containerView addSubview:fromC.view];
[transitionContext containerView addSubview:toVC.view];
toVC.view.frame = transFormedStartFrame;
[UIView animatedWithDuration:AnimationDuration
deley:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
toVC.view.frame = transformedEndFrame;
} completion:^(BOOL finished){
[transitionContext completeTransition:YES];
}];
} else {
[transitionContext containerView addSubview:toVC.view];
[transitionContext.containerView addSubview:fromVC.view];
[UIView animateWithDuration:AnimationDuration delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
fromVC.view.frame = transformedStartFrame;
} completion:^(BOOL finished){
[transitionContext completeTransition:YES];
}];
}
}
@end
具体使用是在视图控制器中实现UINavigationControllerDelegate协议中的navigationController:animationControllerForOperation:fromViewController:toViewController:方法。下面是ViewController实现的部分代码:
@interface ViewController:UITableViewController<UINavigationControllerDelegate>
...
@end
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{
id *animator = <实例化自定义的动画对象>
// operation判定是否视图呈现
animator.presenting = (operation == UINavigationControllerOperationPop)?NO:YES;
return animator;
}
@end
4.2.2 模态导航自定义过渡动画
模态导航自定义过渡动画需要在视图控制器中实现UIViewControllerTransitioningDelegate协议,该协议定义的方法。
animationControllerForPresentedController:presentingController:sourceController:,在呈现视图时候调用
a nimationControllerForDissForDismissedController:,在关闭视图时候调用
这两个方法的返回值都是实现UIViewControllerAnimatedTransitioning协议的自定义过渡动画
下面是ViewController实现的部分代码
@interface ViewController : UITableViewController<UIViewControllerTransitioningDelegate>
...
@end
@implementation ViewController
...
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
id transitioning = <实例化自定义的动画对象>;
return transitioning;
}
- (id<UIViewControllerAnimatedTransitioning>)animatinControllerForDismissedController:(UIViewController *)dismissed
{
id transitioning = <实例化自定义的动画对象>;
return transitioning;
}
@end