以前介绍动画时提过UIView的转场动画,可是开发中咱们碰到更多的viewController的切换,ios中常见的viewcontroller切换有四种:模态视图,导航栏控制器,UITabBarController以及addchildviewcontroller,自定义viewcontroller动画切换也是ios7中的新特性,这里整理下常见的操做,outline以下(本文参考http://onevcat.com/2013/10/vc-transition-in-ios7/,代码下载地址为https://github.com/zanglitao/UIVIewControllerSwitch)html
1:基本介绍ios
2:模态视图自定义动画切换git
3:UINavigationController自定义动画切换github
4:UITaBarController自定义动画切换app
5:模态视图交互式动画切换iview
6:UINavigationController交互式动画切换ide
基本介绍动画
在深刻以前,咱们先来看看新SDK中有关这部份内容的相关接口以及它们的关系和典型用法。这几个接口和类的名字都比较类似,可是仍是能比较好的描述出各自的职能的,一开始的话可能比较迷惑,可是当本身动手实现一两个例子以后,它们之间的关系就会逐渐明晰起来。(相关的内容都定义在UIKit的UIViewControllerTransitioning.h中了)ui
这个接口用来提供切换上下文给开发者使用,包含了从哪一个VC到哪一个VC等各种信息,通常不须要开发者本身实现。具体来讲,iOS7的自定义切换目的之一就是切换相关代码解耦,在进行VC切换时,作切换效果实现的时候必需要须要切换先后VC的一些信息,系统在新加入的API的比较的地方都会提供一个实现了该接口的对象,以供咱们使用。atom
对于切换的动画实现来讲(这里先介绍简单的动画,在后面我会再引入手势驱动的动画),这个接口中最重要的方法有:
这个接口负责切换的具体内容,也即“切换中应该发生什么”。开发者在作自定义切换效果时大部分代码会是用来实现这个接口。它只有两个方法须要咱们实现:
-(NSTimeInterval)transitionDuration:(id < UIViewControllerContextTransitioning >)transitionContext; 系统给出一个切换上下文,咱们根据上下文环境返回这个切换所须要的花费时间(通常就返回动画的时间就行了,SDK会用这个时间来在百分比驱动的切换中进行帧的计算,后面再详细展开)。
-(void)animateTransition:(id < UIViewControllerContextTransitioning >)transitionContext; 在进行切换的时候将调用该方法,咱们对于切换时的UIView的设置和动画都在这个方法中完成。
这个接口的做用比较简单单一,在须要VC切换的时候系统会像实现了这个接口的对象询问是否须要使用自定义的切换效果。这个接口共有四个相似的方法:
-(id< UIViewControllerAnimatedTransitioning >)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
-(id< UIViewControllerAnimatedTransitioning >)animationControllerForDismissedController:(UIViewController *)dismissed;
-(id< UIViewControllerInteractiveTransitioning >)interactionControllerForPresentation:(id < UIViewControllerAnimatedTransitioning >)animator;
-(id< UIViewControllerInteractiveTransitioning >)interactionControllerForDismissal:(id < UIViewControllerAnimatedTransitioning >)animator;
前两个方法是针对动画切换的,咱们须要分别在呈现VC和解散VC时,给出一个实现了UIViewControllerAnimatedTransitioning接口的对象(其中包含切换时长和如何切换)。后两个方法涉及交互式切换,以后再说。
模态视图自定义动画
1:创建一个模态视图控制器
//ModalViewController.h @class ModalViewController; @protocol ModalViewControllerDelegate <NSObject> -(void)dismissViewController:(ModalViewController *)mcv; @end @interface ModalViewController : UIViewController @property(nonatomic,weak)id<ModalViewControllerDelegate> delegate; @end //ModalViewController.m @interface ModalViewController () @end @implementation ModalViewController - (void)viewDidLoad { [super viewDidLoad]; [self.view setBackgroundColor:[UIColor greenColor]]; // Do any additional setup after loading the view. UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; [button addTarget:self action:@selector(dismissViewController) forControlEvents:UIControlEventTouchUpInside]; [button setTitle:@"dismiss" forState:UIControlStateNormal]; [button setFrame:CGRectMake(0, 0, 130, 200)]; [self.view addSubview:button]; } -(void)dismissViewController { [self.delegate dismissViewController:self]; } @end
2:创建主视图控制器,实现ModalViewControllerDelegate协议
//ViewController.h @interface ViewController : UIViewController<ModalViewControllerDelegate> @end //ViewController.m @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; [button addTarget:self action:@selector(pushViewController) forControlEvents:UIControlEventTouchUpInside]; [button setTitle:@"push" forState:UIControlStateNormal]; [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [button setFrame:CGRectMake(0, 0, 130, 200)]; [self.view addSubview:button]; } -(void)pushViewController { ModalViewController *controller = [[ModalViewController alloc] init]; controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical; controller.delegate = self; [self presentViewController:controller animated:YES completion:nil]; } -(void)dismissViewController:(ModalViewController *)mcv { [self dismissViewControllerAnimated:YES completion:nil]; } @end
上面的代码实现了模态视图的切换,经过 controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical还能够设置切换的动画效果,ios内置了几种切换效果供开发者使用,可是咱们如今须要自定义动画效果
3:新建一个类实现UIViewControllerAnimatedTransitioning协议,这个类就是咱们的动画切换类,ios7实现了动画切换类与试图控制器类的解耦,编写一个动画切换类能够反复重用
//ModalTransitionAnimation.h @interface ModalTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning> @end //ModalTransitionAnimation.m @implementation ModalTransitionAnimation //动画持续时间,单位是秒 - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { return 1; } //动画效果 - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext { //经过键值UITransitionContextToViewControllerKey获取须要呈现的视图控制器toVC UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; //获得toVC彻底呈现后的frame CGRect finalFrame = [transitionContext finalFrameForViewController:toVC]; if ([toVC isKindOfClass:[ModalViewController class]]) { //须要呈现的视图是模态视图,此时将模态视图的frame放到屏幕空间下方,这样才能实现从下方弹出的效果 toVC.view.frame = CGRectOffset(finalFrame, 0, [UIScreen mainScreen].bounds.size.height); } else { //须要呈现的视图是主视图,此时将主视图的frame放在屏幕空间上方,这样才能实现从上方放下的效果 toVC.view.frame = CGRectOffset(finalFrame, 0, -[UIScreen mainScreen].bounds.size.height); } //切换在containerView中完成,须要将toVC.view加到containerView中 UIView *containerView = [transitionContext containerView]; [containerView addSubview:toVC.view]; //开始动画,这里使用了UIKit提供的弹簧效果动画,usingSpringWithDamping越接近1弹性效果越不明显,此API在IOS7以后才能使用 [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:0 options:UIViewAnimationOptionCurveEaseIn animations:^{ toVC.view.frame = finalFrame; } completion:^(BOOL finished) { //通知系统动画切换完成 [transitionContext completeTransition:YES]; }]; } @end
上面的代码实现了从屏幕下方弹性弹出ModalViewController以及将ModalViewController弹回屏幕下方的动画效果,上面代码[toVC isKindOfClass:[ModalViewController class]]将动画切换类与视图控制器耦合了起来,实际开发中不是一种好的方式,此处仅仅为了演示
4:从新配置主视图控制器,使用咱们自定义的动画
//ViewController.h //若是须要使用自定义动画,视图须要实现UIViewControllerTransitioningDelegate协议 @interface ViewController : UIViewController<ModalViewControllerDelegate,UIViewControllerTransitioningDelegate> @end //ViewController.m @interface ViewController () @property(nonatomic,strong)ModalTransitionAnimation *animation; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. _animation = [[ModalTransitionAnimation alloc] init]; UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; [button addTarget:self action:@selector(pushViewController) forControlEvents:UIControlEventTouchUpInside]; [button setTitle:@"push" forState:UIControlStateNormal]; [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [button setFrame:CGRectMake(0, 0, 130, 200)]; [self.view addSubview:button]; } -(void)pushViewController { ModalViewController *controller = [[ModalViewController alloc] init]; controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical; controller.delegate = self; controller.transitioningDelegate = self; [self presentViewController:controller animated:YES completion:nil]; } -(void)dismissViewController:(ModalViewController *)mcv { [self dismissViewControllerAnimated:YES completion:nil]; } - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { return _animation; } - (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed { return _animation; } @end
咱们设置了模态视图控制器的transitioningDelegate为self,当present和dismiss模态视图时系统会像实现了这个接口的对象询问是否须要使用自定义的切换效果
这个接口共有四个相似的方法:
-(id< UIViewControllerAnimatedTransitioning >)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
-(id< UIViewControllerAnimatedTransitioning >)animationControllerForDismissedController:(UIViewController *)dismissed;
-(id< UIViewControllerInteractiveTransitioning >)interactionControllerForPresentation:(id < UIViewControllerAnimatedTransitioning >)animator;
-(id< UIViewControllerInteractiveTransitioning >)interactionControllerForDismissal:(id < UIViewControllerAnimatedTransitioning >)animator;
咱们实现了前两个方法用来设置模态视图出现和消失的动画,后两个方法用来处理交互式动画,后面会提到使用方法。
5:运行代码,能够看到模态视图自定义的动画效果
UINavigationController自定义动画切换
除了使用模态视图,导航栏控制器也是使用最多最多见的视图切换方式,咱们也能够自定义导航栏控制器的动画
1:经过storyboard创建一个导航栏控制器
此时运行程序咱们就有两个视图控制器能够切换(storyboard真心强大,特别ios8引入sizeclass后强烈建议使用storyboard)
2:创建咱们的动画切换类
//NavigationTransitionAnimation.h @interface NavigationTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning> @end //NavigationTransitionAnimation.m @implementation NavigationTransitionAnimation //动画持续时间0.7秒 - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { return 0.7; } - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext { //经过键值UITransitionContextToViewControllerKey得到须要呈现的试图控制器 UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; //经过键值UITransitionContextFromViewControllerKey得到须要退出的试图控制器 UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; [[transitionContext containerView] addSubview:toVC.view]; //设置须要呈现的试图控制器透明 [toVC.view setAlpha:0]; //设置须要呈现的试图控制器位于左侧屏幕外,且大小为0.1倍,这样才有从左侧推入屏幕,且逐渐变大的动画效果 toVC.view.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(-[UIScreen mainScreen].bounds.size.width, 0), 0.1, 0.1); [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ //将须要退出的试图控制器移出右侧屏幕外,且大小为原来的0.1倍 fromVC.view.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation([UIScreen mainScreen].bounds.size.width, 0), 0.1, 0.1); fromVC.view.alpha = 0; toVC.view.transform = CGAffineTransformIdentity; toVC.view.alpha = 1; } completion:^(BOOL finished) { //动画结束后属性设为初始值 fromVC.view.transform = CGAffineTransformIdentity; fromVC.view.alpha = 1; //通知系统动画切换成功 [transitionContext completeTransition:YES]; }]; } @end
3:为导航栏控制器添加动画效果,以前为模态视图设置自定义动画时有个协议UIViewControllerTransitioningDelegate,只有设置了协议且实现了协议方法,那么模态视图切换时会使用设置的自定义动画,在导航栏控制器中一样有一个协议定义了一系列方法用来切换视图控制器时询问是否使用自定义方法,那就是UINavigationControllerDelegate
//NavigationControllerDelegate.h @interface NavigationControllerDelegate : NSObject<UINavigationControllerDelegate> @end //NavigationControllerDelegate.m @interface NavigationControllerDelegate() @property (weak, nonatomic) IBOutlet UINavigationController *navigationController; @property(nonatomic,retain)NavigationTransitionAnimation *animation; @end @implementation NavigationControllerDelegate -(void)awakeFromNib { self.animation = [[NavigationTransitionAnimation alloc] init]; } - (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { if (operation == UINavigationControllerOperationPop) { return _animation; } return nil; } @end
上面代码只在pop时使用了自定义动画,固然也能够在push和pop时均使用自定义动画,而且能够为两种操做使用不一样的自定义动画
UINavigationControllerDelegate中有两个方法与视图控制器切换动画相关
- (id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController
- (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
其中第一个方法用于交互式动画
4:连结@property (weak, nonatomic) IBOutlet UINavigationController *navigationController和storyboard中的导航栏控制器,并设置导航栏控制器的delegate
在storyboard中设置delegate的步骤以下:
首先选择object
而后将object拖到navigationcontroller上
而后设置class
最后与navigationcontroller的delegate连结
5:运行代码,能够看到导航栏控制器自定义的动画效果
UITabBarController自定义动画切换
接下来咱们来看第三种常见的试图控制器切换方法:UITabBarController
1:经过storyboard创建一个UITabBarController
2:创建咱们的动画切换类
//TabbarTransitionAnimation.h @interface TabbarTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning> @end //TabbarTransitionAnimation.m #define PERSPECTIVE -1.0/200 @implementation TabbarTransitionAnimation - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { return 0.7; } - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext { CATransform3D viewFromTransform = CATransform3DMakeRotation(M_PI/2, 0, 1, 0); CATransform3D viewToTransform = CATransform3DMakeRotation(-M_PI/2, 0, 1, 0); viewFromTransform.m34 = PERSPECTIVE; viewToTransform.m34 = PERSPECTIVE; UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; UIView *container = [transitionContext containerView]; [toVC.view.layer setAnchorPoint:CGPointMake(0, 0.5)]; [fromVC.view.layer setAnchorPoint:CGPointMake(1, 0.5)]; toVC.view.layer.transform = viewToTransform; [container addSubview:toVC.view]; container.transform = CGAffineTransformMakeTranslation(container.frame.size.width/2.0,0); [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ fromVC.view.layer.transform = viewFromTransform; toVC.view.layer.transform = CATransform3DIdentity; [container setTransform:CGAffineTransformMakeTranslation(-container.frame.size.width/2.0, 0)]; } completion:^(BOOL finished) { fromVC.view.layer.transform = CATransform3DIdentity; toVC.view.layer.transform = CATransform3DIdentity; [fromVC.view.layer setAnchorPoint:CGPointMake(0.5f, 0.5f)]; [toVC.view.layer setAnchorPoint:CGPointMake(0.5f, 0.5f)]; [container setTransform:CGAffineTransformIdentity]; [transitionContext completeTransition:YES]; }]; } @end
以上代码实现了矩形切换的动画效果
3:为UITabBarController添加动画效果,相关的协议是UITabBarControllerDelegate
//TabbarControllerDelegate.h @interface TabbarControllerDelegate : NSObject<UITabBarControllerDelegate> @end //TabbarControllerDelegate.m @interface TabbarControllerDelegate() @property (weak, nonatomic) IBOutlet UITabBarController *tabbarController; @property(nonatomic,strong)TabbarTransitionAnimation *animation; @end @implementation TabbarControllerDelegate - (void)awakeFromNib { _animation = [[TabbarTransitionAnimation alloc] init]; } - (id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0) { return _animation; } @end
4:连结@property (weak, nonatomic) IBOutletUITabBarController *tabbarController和storyboard中的UITabBarController,并设置UITabBarController的delegate(方式和导航栏控制器一致)
5:运行代码,能够看到UITabBarController自定义的动画效果
模态视图交互式动画切换
前面全部的切换都是当点击完一个按钮后就马上执行,可是有时候咱们但愿某些切换操做能够进行到一半取消,好比咱们为前一个模态视图切换提供手势支持,随着手势向下拉,模态视图慢慢退出屏幕底部,但当咱们拉到一半取消或者向上拉,模态视图就会回到以前的状态,这就是所谓的交互式切换
咱们在前一个例子的基础上添加交互式动画切换
1:动画效果类(彻底可使用以前的动画效果类,这里为了说明自定义交互式动画的流程因此从新写一个自定义动画)
//ModalMoveTransitionAnimation.h @interface ModalMoveTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning> @end //ModalMoveTransitionAnimation.m @implementation ModalMoveTransitionAnimation - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { return 1; } - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext { UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; CGRect screenBounds = [[UIScreen mainScreen] bounds]; CGRect initFrame = [transitionContext initialFrameForViewController:fromVC]; CGRect finalFrame = CGRectOffset(initFrame, 0, screenBounds.size.height); UIView *containerView = [transitionContext containerView]; [containerView addSubview:toVC.view]; [containerView sendSubviewToBack:toVC.view]; [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ fromVC.view.frame = finalFrame; } completion:^(BOOL finished) { [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; }]; } @end
上面代码中值得注意的是最后[transitionContext completeTransition:![transitionContext transitionWasCancelled]];由于在交互式切换中切换可能会取消,因此这里使用[transitionContext transitionWasCancelled]判断切换是否成功
2:添加手势
首先咱们须要在刚才的知识基础上补充一些东西:
首先是UIViewControllerContextTransitioning,刚才提到这个是系统提供的VC切换上下文,若是您深刻看了它的头文件描述的话,应该会发现其中有三个关于InteractiveTransition的方法,正是用来处理交互式切换的。可是在初级的实际使用中咱们其实能够不太理会它们,而是使用iOS 7 SDK已经给咱们准备好的一个现成转为交互式切换而新加的类:UIPercentDrivenInteractiveTransition。
这是一个实现了UIViewControllerInteractiveTransitioning接口的类,为咱们预先实现和提供了一系列便利的方法,能够用一个百分比来控制交互式切换的过程。通常来讲咱们更多地会使用某些手势来完成交互式的转移(固然用的高级的话用其余的输入..好比声音,iBeacon距离或者甚至面部微笑来作输入驱动也无不可,毕竟想象无极限嘛..),这样使用这个类(通常是其子类)的话就会很是方便。咱们在手势识别中只须要告诉这个类的实例当前的状态百分好比何,系统便根据这个百分比和咱们以前设定的迁移方式为咱们计算当前应该的UI渲染,十分方便。具体的几个重要方法:
就如上面提到的,UIPercentDrivenInteractiveTransition只是实现了这个接口的一个类。为了实现交互式切换的功能,咱们须要实现这个接口。由于大部分时候咱们其实不须要本身来实现这个接口,所以在这篇入门中就不展开说明了,有兴趣的童鞋能够自行钻研。
还有就是上面提到过的UIViewControllerTransitioningDelegate中的返回Interactive实现对象的方法,咱们一样会在交互式切换中用到它们。
UIPercentDrivenInteractiveTransition能够直接拿来使用,也能够继承它,这里咱们继承它
//ModalInterActiveTransitionAnimation.h @interface ModalInterActiveTransitionAnimation : UIPercentDrivenInteractiveTransition @property(nonatomic,assign)BOOL interacting; - (void)wireToViewController:(UIViewController*)viewController; @end //ModalInterActiveTransitionAnimation.m @interface ModalInterActiveTransitionAnimation() @property (nonatomic, strong) UIViewController *presentingVC; @property (nonatomic, assign) BOOL shouldComplete; @end @implementation ModalInterActiveTransitionAnimation - (void)wireToViewController:(UIViewController*)viewController{ _presentingVC = viewController; //添加手势 UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)]; [viewController.view addGestureRecognizer:gesture]; } -(CGFloat)completionSpeed { return 1 - self.percentComplete; } -(void)handleGesture:(UIPanGestureRecognizer *)gestureRecognizer { CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view.superview]; switch (gestureRecognizer.state) { case UIGestureRecognizerStateBegan:{ _interacting = YES; [self.presentingVC dismissViewControllerAnimated:YES completion:nil]; break; } case UIGestureRecognizerStateChanged: { CGFloat fraction = translation.y / 400.0; fraction = fminf(fmaxf(fraction, 0.0), 1.0); _shouldComplete = (fraction > 0.5); [self updateInteractiveTransition:fraction]; break; } case UIGestureRecognizerStateEnded: case UIGestureRecognizerStateCancelled: { _interacting = NO; if (!_shouldComplete || gestureRecognizer.state == UIGestureRecognizerStateCancelled || [gestureRecognizer velocityInView:gestureRecognizer.view].y < 0 ) { [self cancelInteractiveTransition]; } else { [self finishInteractiveTransition]; } break; } default: break; } } @end
上面代码中的interacting用来判断当前是否处于交互式视图切换过程当中,只有处在这个过程当中咱们才须要在
- (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator
使用动画
3:添加交互式动画切换
//ViewController.m @interface ViewController () @property(nonatomic,strong)ModalTransitionAnimation *animation; @property(nonatomic,strong)ModalInterActiveTransitionAnimation *interActive; @property(nonatomic,strong)ModalMoveTransitionAnimation *interActiveAnimation; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. _animation = [[ModalTransitionAnimation alloc] init]; _interActive = [[ModalInterActiveTransitionAnimation alloc] init]; _interActiveAnimation = [[ModalMoveTransitionAnimation alloc] init]; UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; [button addTarget:self action:@selector(pushViewController) forControlEvents:UIControlEventTouchUpInside]; [button setTitle:@"push" forState:UIControlStateNormal]; [button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [button setFrame:CGRectMake(0, 0, 130, 200)]; [self.view addSubview:button]; } -(void)pushViewController { ModalViewController *controller = [[ModalViewController alloc] init]; controller.modalTransitionStyle = UIModalTransitionStyleCoverVertical; controller.delegate = self; controller.transitioningDelegate = self; [_interActive wireToViewController:controller]; [self presentViewController:controller animated:YES completion:nil]; } -(void)dismissViewController:(ModalViewController *)mcv { [self dismissViewControllerAnimated:YES completion:nil]; } - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { return _animation; } - (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed { return self.interActive.interacting ? _interActiveAnimation : _animation; } - (id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator { return self.interActive.interacting ? self.interActive : nil; } @end
回顾上面的流程,咱们为view添加手势,而后在手势中根据状况调用UIPercentDrivenInteractiveTransition的三个方法(updateInteractiveTransition,cancelInteractiveTransition,finishInteractiveTransition),最后在interactionControllerForDismissal中返回咱们定义的动画,这样就能够实现交互式动画切换了
UINavigationController交互式动画切换
与上面的流程类似
1:添加手势,在手势中调用UIPercentDrivenInteractiveTransition的三个方法
2:在相应的代理方法中返回咱们定义的动画
这里咱们不用继承UIPercentDrivenInteractiveTransition,而是直接使用它,同时动画也直接使用以前在导航栏控制器切换中定义好的动画,不过代码须要稍加修改
//NavigationTransitionAnimation.m @implementation NavigationTransitionAnimation //动画持续时间0.7秒 - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { return 0.7; } - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext { //经过键值UITransitionContextToViewControllerKey得到须要呈现的试图控制器 UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; //经过键值UITransitionContextFromViewControllerKey得到须要退出的试图控制器 UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; [[transitionContext containerView] addSubview:toVC.view]; //设置须要呈现的试图控制器透明 [toVC.view setAlpha:0]; //设置须要呈现的试图控制器位于左侧屏幕外,且大小为0.1倍,这样才有从左侧推入屏幕,且逐渐变大的动画效果 toVC.view.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation(-[UIScreen mainScreen].bounds.size.width, 0), 0.1, 0.1); [UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^{ //将须要退出的试图控制器移出右侧屏幕外,且大小为原来的0.1倍 fromVC.view.transform = CGAffineTransformScale(CGAffineTransformMakeTranslation([UIScreen mainScreen].bounds.size.width, 0), 0.1, 0.1); fromVC.view.alpha = 0; toVC.view.transform = CGAffineTransformIdentity; toVC.view.alpha = 1; } completion:^(BOOL finished) { //动画结束后属性设为初始值 fromVC.view.transform = CGAffineTransformIdentity; fromVC.view.alpha = 1; toVC.view.transform = CGAffineTransformIdentity; toVC.view.alpha = 1; [transitionContext completeTransition:![transitionContext transitionWasCancelled]]; }]; } @end
咱们修改了最后几行代码,让它适应交互式切换
添加咱们的手势以及自定义交互动画
@interface NavigationControllerDelegate() @property (weak, nonatomic) IBOutlet UINavigationController *navigationController; @property(nonatomic,retain)NavigationTransitionAnimation *animation; @property (strong, nonatomic) UIPercentDrivenInteractiveTransition* interactionController; @property (nonatomic,assign)BOOL interActiving; @end @implementation NavigationControllerDelegate -(void)awakeFromNib { self.animation = [[NavigationTransitionAnimation alloc] init]; self.interactionController = [[UIPercentDrivenInteractiveTransition alloc] init]; [self.navigationController.view addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleGesture:)]]; } -(void)handleGesture:(UIPanGestureRecognizer *)gesture { UIView* view = self.navigationController.view; CGPoint location = [gesture locationInView:gesture.view]; CGPoint translation = [gesture translationInView:gesture.view]; switch (gesture.state) { case UIGestureRecognizerStateBegan: { _interActiving = YES; if (location.x < CGRectGetMidX(view.bounds) && self.navigationController.viewControllers.count > 1) { [self.navigationController popViewControllerAnimated:YES]; } break; } case UIGestureRecognizerStateChanged: { CGFloat fraction = fabs(translation.x / view.bounds.size.width); [_interactionController updateInteractiveTransition:fraction]; break; } case UIGestureRecognizerStateCancelled: case UIGestureRecognizerStateEnded: { _interActiving = NO; CGFloat fraction = fabs(translation.x / view.bounds.size.width); if (fraction < 0.5 || [gesture velocityInView:view].x < 0 || gesture.state == UIGestureRecognizerStateCancelled) { [_interactionController cancelInteractiveTransition]; } else { [_interactionController finishInteractiveTransition]; } break; } default: break; } } - (id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { if (operation == UINavigationControllerOperationPop) { return _animation; } return nil; } - (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController { return self.interActiving ? self.interactionController : nil; } @end
最后
在ios7以后导航栏控制器自带了一个交互式切换动画,咱们只要从屏幕左侧向右滑就能回到上一层
咱们能够自定义这个手势是否开启(默认开启)
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
但当咱们定义了leftBarButtonItem后这个手势每每会失效,解决方法能够看这篇博客