转场动画UINavigationControllerDelegate
从iOS7开始,苹果更新了自定义ViewController转场的API,这些新增的类和接口让很多人困惑,望而却步。本文就从这些API入口,让读者理清这些API错综复杂的关系。
几个protocol
讲自定义转场就离不开这几个protocol:
UIViewControllerContextTransitioning
UIViewControllerAnimatedTransitioning
UIViewControllerInteractiveTransitioning
UIViewControllerTransitioningDelegate
UINavigationControllerDelegate
UITabBarControllerDelegate
乍一看很多,其实很简单,我们可以将其分为三类:
- 描述ViewController转场的:
UIViewControllerTransitioningDelegate
,UINavigationControllerDelegate
,UITabBarControllerDelegate
- 定义动画内容的
UIViewControllerAnimatedTransitioning
,UIViewControllerInteractiveTransitioning
- 表示动画上下文的
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);
两个方法分别返回UIViewControllerInteractiveTransitioning
和UIViewControllerAnimatedTransitioning
,它们的任务是描述动画行为(转场动画如何执行,就看它俩的)。
从名字可以看出,这两个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
这是一个缩放同时修改透明度的动画,我们来看下如何实现。
在上面的讲解中,我们通过倒推的方式来理解转场动画中用到的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相关推荐
- iOS 自定义转场动画初探
最近项目刚迭代,正好闲下来捣鼓了一下iOS的自定义转场的效果.闲话不多说,直接开始上代码吧.(ps:请忽略实际的转场效果,关注技术本身呢哦.pps:主要是转场的动画做的比较low啦!) 1.首先定义一 ...
- (0030) iOS 开发之跳转之转场动画
demo: 1 iOS ~ ViewController的Push,Pop和Present,Dismiss转场动画 // http://blog.csdn.net/zhangao0086/artic ...
- VCTransitionsLibrary –自定义iOS交互式转场动画的库
简介 VCTransitionsLibrary 提供了许多适用于入栈,出栈,模态等场景下控制器切换时的转场动画.它本身提供了一个定义好的转场动画库,你可以拖到自己工程中直接使用;也提供了许多拥有不同转 ...
- iOS自定义转场动画实战讲解
转场动画这事,说简单也简单,可以通过presentViewController:animated:completion:和dismissViewControllerAnimated:completio ...
- 【Swift学习笔记-《PRODUCT》读书记录-实现自定义转场动画】
iOS默认的push动画是把即将展示的控制器从右边推过来.有时我们想实现类似PPT中的一些动画,这时候就需要自定义转场动画了.如下图我们想实现一个淡出并且放大的过场动画,在退出时是一个淡出缩小的动画. ...
- iOS 自定义转场动画浅谈
代码地址如下: http://www.demodashi.com/demo/11612.html 路漫漫其修远兮,吾将上下而求索 前记 想研究自定义转场动画很久了,时间就像海绵,挤一挤还是有的,花了差 ...
- 控制器之间跳转实现转场动画,动画控制器概念
在iOS7以前,开发者如果希望定制导航控制器推入推出视图时的转场动画,一般都只能通过子类化UINavigationController或者自己编写动画代码去覆盖相应的方法,现在iOS7为开发者带来了福 ...
- iOS 自定义转场动画篇
前言: 自定义转场动画其实并不难, 关键在于能够明白思路, 也就是操作步骤. 本篇博客主要以present转场动画为例, 进行分析, 操作, 如有错误欢迎简信与我交流. 不进行修改的话, presen ...
- iOS 关于自定义转场动画,以UITabBarController为例
1.小记 关于自定义转场动画,只要你理清他的"套路",你就可以随心所欲地自定义了. 大体思路就是:遵守对应的代理协议,然后设置对应的代理,实现代理方法,这个代理方法要返回的值就是你 ...
- iOS开发 tabbar自定义转场动画
1.小记 关于自定义转场动画,只要你理清他的"套路",你就可以随心所欲地自定义了. 大体思路就是:遵守对应的代理协议,然后设置对应的代理,实现代理方法,这个代理方法要返回的值就是你 ...
最新文章
- 这一行代码,很有温度!
- 机器人过程自动化的10个秘密
- 新手用python2还是3-Python 使用情况调查:2还是3?(附致歉声明)
- 说明使用assert和防错代码的区别
- Spring-Aop-XML实现
- 关于java的volatile
- mysql创建分区是否存在_mysql中如何判断是否支持分区
- 误操作导致系统只剩下lo
- 轻松实现SpringBoot项目异常全局处理
- 吴恩达机器学习(八)偏差与方差
- 博图能打开s7200吗_透明胶真的能把车窗打开吗?现场实验给你看,能成功吗?...
- CentOS 上MySQL报错Can't connect to local Mysql server through socket '/tmp/mysql.scok' (111)
- 转自weiphone]在美做开发多年,写给国内apple开发新手的一些心得,无教程
- 通过Kali linux 模拟CC攻击进行WEB压力测试实战
- 微信小程序安装moment报错VM182 WAService.js:2 Error: module “pages/mine/myWish/detail/moment.js“ is not define
- STM32入门——什么是STM32
- 无线传感器网络中的MAC协议
- 如何修改Android应用程序的图标和名字
- yso之Commons Collections
- Cent0S7安装oracle11g