从iOS7开始,苹果更新了自定义ViewController转场的API,这些新增的类和接口让很多人困惑,望而却步。本文就从这些API入口,让读者理清这些API错综复杂的关系。

几个protocol

讲自定义转场就离不开这几个protocol:

UIViewControllerContextTransitioning
UIViewControllerAnimatedTransitioning
UIViewControllerInteractiveTransitioning
UIViewControllerTransitioningDelegate
UINavigationControllerDelegate
UITabBarControllerDelegate

乍一看很多,其实很简单,我们可以将其分为三类:

  1. 描述ViewController转场的:
    UIViewControllerTransitioningDelegate,UINavigationControllerDelegate,UITabBarControllerDelegate
  2. 定义动画内容的
    UIViewControllerAnimatedTransitioning,UIViewControllerInteractiveTransitioning
  3. 表示动画上下文的
    UIViewControllerContextTransitioning

描述ViewController转场的

细说之前先扯个蛋:

为什么苹果要引入这一套API?因为在iOS7之前,做转场动画很麻烦,要写一大堆代码在ViewController中。引入这一套API之后,在丰富功能的同时极大程度地降低了代码耦合,实现方式就是将之前在ViewController里面的代码通过protocol分离了出来。

顺着这个思路往下想,实现自定义转场动画首先需要找到ViewController的delegate。苹果告诉我们切换ViewController有三种形式:UITabBarController内部切换,UINavigationController切换,present modal ViewController。这三种方式是不是需要不同的protocol呢?

我们分别来看下:

  • UIViewControllerTransitioningDelegate自定义模态转场动画时使用。
    设置UIViewController的属性transitioningDelegate。
    @property (nullable, nonatomic, weak) id <UIViewControllerTransitioningDelegate> transitioningDelegate
  • UINavigationControllerDelegate自定义navigation转场动画时使用。
    设置UINavigationController的属性delegate
    @property(nullable, nonatomic, weak) id<UINavigationControllerDelegate> delegate
  • UITabBarControllerDelegate自定义tab转场动画时使用。
    设置UITabBarController的属性delegate
    @property(nullable, nonatomic,weak) id<UITabBarControllerDelegate> delegate

实际上这三个protocol干的事情是一样的,就是我们“扯淡”的内容,只不过他们的应用场景不同罢了。我们下面以UINavigationControllerDelegate为例,其他的类似。

定义动画内容的

UINavigationControllerDelegate主要包含这两个方法:

- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationControllerinteractionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController NS_AVAILABLE_IOS(7_0);- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationControlleranimationControllerForOperation:(UINavigationControllerOperation)operationfromViewController:(UIViewController *)fromVCtoViewController:(UIViewController *)toVC  NS_AVAILABLE_IOS(7_0);

两个方法分别返回UIViewControllerInteractiveTransitioningUIViewControllerAnimatedTransitioning,它们的任务是描述动画行为(转场动画如何执行,就看它俩的)。
从名字可以看出,这两个protocol的区别在于是否是interactive的。如何理解?****interactive动画可以根据输入信息的变化改变动画的进程。****例如iOS系统为UINavigationController提供的默认右滑退出手势就是一个interactive 动画,整个动画的进程由用户手指的移动距离控制。

我们来看下相对简单的UIViewControllerAnimatedTransitioning:

- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext; @optional - (void)animationEnded:(BOOL) transitionCompleted; 

transitionDuration返回动画的执行时间,animateTransition处理具体的动画,animationEnded是optional,大部分情况下不需要处理。
这里出现了我们要讲的最后一个protocol:UIViewControllerContextTransitioning

表示动画上下文的

UIViewControllerContextTransitioning也是唯一一个不需要我们实现的protocol。

Do not adopt this protocol in your own classes, nor should you directly create objects that adopt this protocol.

UIViewControllerContextTransitioning提供了一系列方法,为interactive和非interactive动画提供上下文:

//转场动画发生在该View中
- (nullable UIView *)containerView;
//上报动画执行完毕
- (void)completeTransition:(BOOL)didComplete; //根据key返回一个ViewController。我们通过UITransitionContextFromViewControllerKey找到将被替换掉的ViewController,通过UITransitionContextToViewControllerKey找到将要显示的ViewController - (nullable __kindof UIViewController *)viewControllerForKey:(NSString *)key; 

还有一些其他的方法,我们以后用到再说。

下面我们通过一个简单的Demo串联理解下。

DEMO

transitionDemo.gif

这是一个缩放同时修改透明度的动画,我们来看下如何实现。
在上面的讲解中,我们通过倒推的方式来理解转场动画中用到的protocol,在Demo 中,我们会从创建动画开始。

第一步:创建动画

由上面的解析得知,动画是在UIViewControllerAnimatedTransitioning中定义的,所以我们首先创建实现UIViewControllerAnimatedTransitioning的对象:JLScaleTransition

JLScaleTransition.h

@interface JLScaleTransition : NSObject<UIViewControllerAnimatedTransitioning>
@end
JLScaleTransition.m@implementation JLScaleTransition- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext
{return 0.5f;
}- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{UIViewController *toVC = (UIViewController*)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];UIViewController *fromVC = (UIViewController*)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];UIView * containerView = [transitionContext containerView];UIView * fromView = fromVC.view;UIView * toView = toVC.view;[containerView addSubview:toView];[[transitionContext containerView] bringSubviewToFront:fromView];NSTimeInterval duration = [self transitionDuration:transitionContext];[UIView animateWithDuration:duration animations:^{fromView.alpha = 0.0;fromView.transform = CGAffineTransformMakeScale(0.2, 0.2);toView.alpha = 1.0;} completion:^(BOOL finished) {fromView.transform = CGAffineTransformMakeScale(1, 1);[transitionContext completeTransition:YES];}];
}
在animateTransition中,我们分别获取两个ViewController的view,将toView添加到containerView中,然后执行动画。为了理解containerView和fromView,toView的关系,我们添加几个log来分析一下:- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
{UIViewController *toVC = (UIViewController*)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];UIViewController *fromVC = (UIViewController*)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];UIView * containerView = [transitionContext containerView];UIView * fromView = fromVC.view;UIView * toView = toVC.view;NSLog(@"startAnimation! fromView = %@", fromView);NSLog(@"startAnimation! toView = %@", toView);for(UIView * view in containerView.subviews){NSLog(@"startAnimation! list container subviews: %@", view);}[containerView addSubview:toView];[[transitionContext containerView] bringSubviewToFront:fromView];NSTimeInterval duration = [self transitionDuration:transitionContext];[UIView animateWithDuration:duration animations:^{fromView.alpha = 0.0;fromView.transform = CGAffineTransformMakeScale(0.2, 0.2);toView.alpha = 1.0;} completion:^(BOOL finished) {fromView.transform = CGAffineTransformMakeScale(1, 1);[transitionContext completeTransition:YES];for(UIView * view in containerView.subviews){NSLog(@"endAnimation! list container subviews: %@", view);}}];
}

运行log如下:
2016-06-29 13:50:48.512 JLTransition[1970:177922] startAnimation! fromView = <UIView: 0x7aaef4e0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x7aaef5a0>> 2016-06-29 13:50:48.513 JLTransition[1970:177922] startAnimation! toView = <UIView: 0x796ac5a0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x796ac050>> 2016-06-29 13:50:48.513 JLTransition[1970:177922] startAnimation! list container subviews: <UIView: 0x7aaef4e0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x7aaef5a0>> 2016-06-29 13:50:49.017 JLTransition[1970:177922] endAnimation! list container subviews: <UIView: 0x796ac5a0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x796ac050>> 

可见,转场执行的时候,containerView中只包含fromView,转场动画执行完毕之后,containerView会将fromView移除。因为containerView不负责toView的添加,所以我们需要主动将toView添加到containerView中。

注意!非interactive转场中,动画结束之后需要执行[transitionContext completeTransition:YES];(如果动画被取消,传NO);但是在interactive转场中,动画是否结束是由外界控制的(用户行为或者特定函数),需要在外部调用。

第二步:定义转场

在第二部,我们需要实现UIViewControllerAnimatedTransitioning,并将第一步创建的JLScaleTransition对象返回。

JLScaleNavControlDelegate.h

@interface JLScaleNavControlDelegate : NSObject<UINavigationControllerDelegate>
@end

JLScaleNavControlDelegate.m

@implementation JLScaleNavControlDelegate
- (nullable id <UIViewControllerAnimatedTransitioning>) navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
{return [JLScaleTransition new];
}
@end

这一步很简单,实现UIViewControllerAnimatedTransitioning对应方法即可。

第三步:设置转场

设置转场其实就是设置delegate(还记得我们“扯淡”的内容吧)。

   self.navigationController.delegate = id<UINavigationControllerDelegate>self.transitioningDelegate = id<UIViewControllerTransitioningDelegate>self.tabBarController.delegate = id<UITabBarControllerDelegate>

设置delegate有两种方式:通过代码;通过StoryBoard。

通过代码设置

@interface ViewController ()
@property (nonatomic, strong) JLScaleNavControlDelegate * scaleNavDelegate;
@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];self.scaleNavDelegate = [JLScaleNavControlDelegate new];
}- (IBAction)triggerTransitionDelegate:(id)sender
{self.navigationController.delegate = self.scaleNavDelegate;[self.navigationController pushViewController:[TargetViewController new] animated:YES];
}

链接:https://www.jianshu.com/p/e7155f938e59

如果需要更多效果的话,参考https://github.com/alanwangmodify/WXSTransition

转载于:https://www.cnblogs.com/hualuoshuijia/p/9935055.html

转场动画UINavigationControllerDelegate相关推荐

  1. iOS 自定义转场动画初探

    最近项目刚迭代,正好闲下来捣鼓了一下iOS的自定义转场的效果.闲话不多说,直接开始上代码吧.(ps:请忽略实际的转场效果,关注技术本身呢哦.pps:主要是转场的动画做的比较low啦!) 1.首先定义一 ...

  2. (0030) iOS 开发之跳转之转场动画

    demo: 1 iOS  ~ ViewController的Push,Pop和Present,Dismiss转场动画 // http://blog.csdn.net/zhangao0086/artic ...

  3. VCTransitionsLibrary –自定义iOS交互式转场动画的库

    简介 VCTransitionsLibrary 提供了许多适用于入栈,出栈,模态等场景下控制器切换时的转场动画.它本身提供了一个定义好的转场动画库,你可以拖到自己工程中直接使用;也提供了许多拥有不同转 ...

  4. iOS自定义转场动画实战讲解

    转场动画这事,说简单也简单,可以通过presentViewController:animated:completion:和dismissViewControllerAnimated:completio ...

  5. 【Swift学习笔记-《PRODUCT》读书记录-实现自定义转场动画】

    iOS默认的push动画是把即将展示的控制器从右边推过来.有时我们想实现类似PPT中的一些动画,这时候就需要自定义转场动画了.如下图我们想实现一个淡出并且放大的过场动画,在退出时是一个淡出缩小的动画. ...

  6. iOS 自定义转场动画浅谈

    代码地址如下: http://www.demodashi.com/demo/11612.html 路漫漫其修远兮,吾将上下而求索 前记 想研究自定义转场动画很久了,时间就像海绵,挤一挤还是有的,花了差 ...

  7. 控制器之间跳转实现转场动画,动画控制器概念

    在iOS7以前,开发者如果希望定制导航控制器推入推出视图时的转场动画,一般都只能通过子类化UINavigationController或者自己编写动画代码去覆盖相应的方法,现在iOS7为开发者带来了福 ...

  8. iOS 自定义转场动画篇

    前言: 自定义转场动画其实并不难, 关键在于能够明白思路, 也就是操作步骤. 本篇博客主要以present转场动画为例, 进行分析, 操作, 如有错误欢迎简信与我交流. 不进行修改的话, presen ...

  9. iOS 关于自定义转场动画,以UITabBarController为例

    1.小记 关于自定义转场动画,只要你理清他的"套路",你就可以随心所欲地自定义了. 大体思路就是:遵守对应的代理协议,然后设置对应的代理,实现代理方法,这个代理方法要返回的值就是你 ...

  10. iOS开发 tabbar自定义转场动画

    1.小记 关于自定义转场动画,只要你理清他的"套路",你就可以随心所欲地自定义了. 大体思路就是:遵守对应的代理协议,然后设置对应的代理,实现代理方法,这个代理方法要返回的值就是你 ...

最新文章

  1. 这一行代码,很有温度!
  2. 机器人过程自动化的10个秘密
  3. 新手用python2还是3-Python 使用情况调查:2还是3?(附致歉声明)
  4. 说明使用assert和防错代码的区别
  5. Spring-Aop-XML实现
  6. 关于java的volatile
  7. mysql创建分区是否存在_mysql中如何判断是否支持分区
  8. 误操作导致系统只剩下lo
  9. 轻松实现SpringBoot项目异常全局处理
  10. 吴恩达机器学习(八)偏差与方差
  11. 博图能打开s7200吗_透明胶真的能把车窗打开吗?现场实验给你看,能成功吗?...
  12. CentOS 上MySQL报错Can't connect to local Mysql server through socket '/tmp/mysql.scok' (111)
  13. 转自weiphone]在美做开发多年,写给国内apple开发新手的一些心得,无教程
  14. 通过Kali linux 模拟CC攻击进行WEB压力测试实战
  15. 微信小程序安装moment报错VM182 WAService.js:2 Error: module “pages/mine/myWish/detail/moment.js“ is not define
  16. STM32入门——什么是STM32
  17. 无线传感器网络中的MAC协议
  18. 如何修改Android应用程序的图标和名字
  19. yso之Commons Collections
  20. Cent0S7安装oracle11g

热门文章

  1. 三、MQTT Windosw下的 进一步调试
  2. MySQL集群(四)之keepalived实现mysql双主高可用
  3. description方法 oc 就是NSLog使用
  4. XStream使用总结
  5. sql2005各种版本
  6. 2018.9.19作业
  7. Django路由控制
  8. Eclipse使用Maven创建web3.0项目
  9. Linux下selinux简单梳理
  10. redis搭建 (centos)并测试验证