自定义Push和Pop过渡动画
一、效果和源码
本文介绍如何实现一个NavigationController的自定义Push和Pop过渡动画,运行效果如下:
二、准备工作
首先,新建两个ViewController的实例,分别为FirstViewController和SecondViewController,在FirstViewController中包含一个TableView列表,每个cell都展示了一张图片和标题,当用户点击任意cell时,将跳转到SecondViewController,并且cell中图片将以动画的形式移动到新的ViewController中,FirstViewController和SecondViewController的布局实现细节就不再累赘。
为了让UINavigationController使用我们自定义的过渡动画而不是系统默认的动画,首先需要让FirstViewController遵循UINavigationControllerDelegate协议,并在viewDidAppear:中设置当前controller为navigationController的代理对象:
- (void)viewDidAppear:(BOOL)animated {[super viewDidAppear:animated];self.navigationController.delegate = self;
}
我们还应该在controller不可见时取消其作为navigation control的代理对象。
- (void)viewWillDisappear:(BOOL)animated {[super viewWillDisappear:animated];if (self.navigationController.delegate == self) {self.navigationController.delegate = nil;}
}
当使用push或pop将controller从navigation压栈或者出栈时,它都会询问它的代理来获得一个遵循UIViewControllerAnimatedTransitioning协议的对象,我们把这个对象命名为FirstTransition,我们只要实现UINavigationControllerDelegate的以下方法,返回FirstTransiton即可。
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationControlleranimationControllerForOperation:(UINavigationControllerOperation)operationfromViewController:(UIViewController *)fromVCtoViewController:(UIViewController *)toVC {if (fromVC == self && [toVC isKindOfClass:[SecondViewController class]]) {return [[FirstTransition alloc] init];}else {return nil;}
}
FirstTransition现在报错,因为我们还没有定义这个类,那么我们新建一个NSObject的子类,并遵循UIViewControllerAnimatedTransitioning协议,命名为FirstTransition。
现在我们运行项目,并没有什么效果,而且FirstTransition有警告,说我们还没实现UIViewControllerAnimatedTransitioning的协议方法。因为我们必须实现此代理的这两个方法:animateTransition:和transitionDuration:
transitionDuration:只需要返回过渡动画的时间:
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {return 0.3;
}
animateTransition:方法是本文的核心,它将负责处理整个过渡的动画方式。它传递了一个包含了我们需要用到的几个类的参数,还提供了下面这些方法
• viewControllerForKey:获得过渡的两个controllers
• containerView容纳两个viewcontroller的容器view
• initialFrameForViewController: 和finalFrameForViewController每个controller的view的开始和结束位置。
我们看下animateTransition:的实现,我们首先分别获得过渡的两个controller以及容器view的指针。
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {FirstViewController *fromViewController = (FirstViewController*)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];SecondViewController *toViewController = (SecondViewController*)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];UIView *containerView = [transitionContext containerView];NSTimeInterval duration = [self transitionDuration:transitionContext];
然后,我们获得要过渡的cell,获得imageview的快照,push操作时,我们移动这个快照和改变它的大小,同时隐藏cell的imageview,让人看着就像是imageview在移动。
//获得cell中的图片的快照JXTableViewCell *cell = (JXTableViewCell*)[fromViewController.tableView cellForRowAtIndexPath:[fromViewController.tableView indexPathForSelectedRow]];UIView *cellImageSnapshot = [cell.leftImageView snapshotViewAfterScreenUpdates:NO];cellImageSnapshot.frame = [containerView convertRect:cell.leftImageView.frame fromView:cell.leftImageView.superview];cell.leftImageView.hidden = YES;
再然后,我们设置第二个controller的view,将它设置为透明并将其放置在最终的位置上。我们会使它在过渡过程中逐渐出现。
//设置初始view的状态toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController];toViewController.view.alpha = 0;toViewController.imageView.hidden = YES;[containerView addSubview:toViewController.view];[containerView addSubview:cellImageSnapshot];
现在我们开始编写view的动画,移动图片的快照、逐渐显示第二个controller的view,在动画结束的block中,移除快照并显示我们之前隐藏了的view,最后,我们需要调用completeTransition:来通知过渡上下文过渡已经完成。
[UIView animateWithDuration:duration animations:^{toViewController.view.alpha = 1.0;CGRect frame = [containerView convertRect:toViewController.imageView.frame fromView:toViewController.view];cellImageSnapshot.frame = frame;}completion:^(BOOL finished) {toViewController.imageView.hidden = NO;cell.leftImageView.hidden = NO;[cellImageSnapshot removeFromSuperview];[transitionContext completeTransition:!transitionContext.transitionWasCancelled];}];
}
至此,我们运行项目,可以看到从FirstViewController跳转到SecondViewController时,将会看到图片移动的动画效果。
五、Pop动画
不过当我们点击返回时,仍然是系统默认动画。我们只要参照前面push的实现,既首先让SecondViewController遵循UINavigationControllerDelegate协议,实现UINavigationControllerDelegate的方法,并返回一个继承自NSObject、遵循UIViewControllerAnimatedTransitioning协议的实例(Demo中的SecondTransition)。在SecondTransition中实现transitionDuration: 和animateTransition:方法,分别返回过渡的时间、设置动画效果。详见Demo源码。
六、使用过渡交互
现在我们让用户可以通过屏幕左边进行交互,我们需要用到iOS7中的UIScreenEdgePanGestureRecognizer类。
我们在secondViewController的viewDidLoad方法中创建UIScreenEdgePanGestureRecognizer
- (void)viewDidLoad {[super viewDidLoad];...UIScreenEdgePanGestureRecognizer *popRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePopRecognizer:)];popRecognizer.edges = UIRectEdgeLeft;[self.view addGestureRecognizer:popRecognizer];
}
现在我们可以使用监听到的手势事件,更新另一个类:UIPercentDrivenInteractiveTransition,这将根据我们的手势改变过渡动画进度。
当手势开始,我们创建和存储一个UIPercentDrivenInteractiveTransition实例,然后通知navigation controller来弹栈。
当手势改变,我们根据手势的进度来更新UIPercentDrivenInteractiveTransition。
当手势结束,如果手势拖动足够大,过渡动画执行完成,或者其它情况如取消过渡等,我们根据情况调用finishInteractiveTransition或者cancelInteractiveTransition。
- (void)handlePopRecognizer:(UIScreenEdgePanGestureRecognizer*)recognizer {// Calculate how far the user has dragged across the viewCGFloat progress = [recognizer translationInView:self.view].x / (self.view.bounds.size.width * 1.0);progress = MIN(1.0, MAX(0.0, progress));if (recognizer.state == UIGestureRecognizerStateBegan) {// Create a interactive transition and pop the view controllerself.interactivePopTransition = [[UIPercentDrivenInteractiveTransition alloc] init];[self.navigationController popViewControllerAnimated:YES];}else if (recognizer.state == UIGestureRecognizerStateChanged) {// Update the interactive transition's progress[self.interactivePopTransition updateInteractiveTransition:progress];}else if (recognizer.state == UIGestureRecognizerStateEnded || recognizer.state == UIGestureRecognizerStateCancelled) {// Finish or cancel the interactive transitionif (progress > 0.5) {[self.interactivePopTransition finishInteractiveTransition];}else {[self.interactivePopTransition cancelInteractiveTransition];}self.interactivePopTransition = nil;}
}
现在我们创建和更新UIPercentDrivenInteractiveTransition实例,我们还需要告诉navigation controller来使用它,在SecondViewController中添加:
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationControllerinteractionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {// Check if this is for our custom transitionif ([animationController isKindOfClass:[DSLTransitionFromSecondToFirst class]]) {return self.interactivePopTransition;}else {return nil;}
}
运行Demo,在secondViewController中,在屏幕左边缘往中间水平滑动,将可以看到过渡进度随着我们的手势而不同。
参考文献:
自定义Push和Pop过渡动画相关推荐
- iOS精仿唱吧下载按钮、仿知乎日报、自定义提示视图、过渡动画、记录应用等源码
iOS精选源码 IOS之分段控制器OC/Swift通用 Swift4.0_组件化:APP启动引导页页 精仿唱吧App音乐下载按钮,完美快速集成使用 iOS指纹解锁和手势解锁源码 OC _纯代码颜色选择 ...
- iOS精仿唱吧下载按钮、仿知乎日报、自定义提示视图、过渡动画、记录应用等源码...
iOS精选源码 IOS之分段控制器OC/Swift通用 Swift4.0_组件化:APP启动引导页页 精仿唱吧App音乐下载按钮,完美快速集成使用 iOS指纹解锁和手势解锁源码 OC _纯代码颜色选择 ...
- android自定义过渡动画,11.自定义过渡动画
11.1 问题 应用程序需要自定义Activity切换或Fragment切换时产生的过渡动画. 11.2 解决方案 (API Level 5) 要修改Activity间的过渡动画,可以使用overri ...
- iOS自定义过渡动画
历时5天从各种英文教程中学习到的过渡动画,是一个很难忘的探索经历 比较好的参考文章自定义UIViewController过渡入门 ,动画入门. 转场方式 首先让我们来了解iOS转场的方式: UINav ...
- 【转】更改navigationController push和pop界面切换动画
为什么80%的码农都做不了架构师?>>> 原文:http://blog.csdn.net/longlongago2000/article/details/7589706 有时候 ...
- 二、Vue(发送AJAX请求、Vue生命周期、计算属性、属性和方法、自定义指令、过渡(动画))
一. 发送AJAX请求 1. 简介 vue本身不支持发送AJAX请求,需要使用vue-resource.axios等插件实现 axios是一个基于Promise的HTTP请求客户端,用 ...
- iOS自定义转场动画(1)——自定义Push转场动画
版本:Xcode 7.0.1 语言:Objective-C 转场动画就是viewController之间切换的动画. 主要有以下三种自定义方法: 列Push & Pop Modal Segue ...
- iOS学习笔记-自定义过渡动画
代码地址如下: http://www.demodashi.com/demo/11678.html 这篇笔记翻译自raywenderlick网站的过渡动画的一篇文章,原文用的swift,由于考虑到swi ...
- 手机端 自定义简单的下拉动画效果选择框 解决过渡失效
手机端 自定义简单的下拉动画效果选择框 解决过渡失效 <template> // 弹出框触发 并显示选择结果<view class="header_top_sel" ...
最新文章
- MySQL 源码 需要 什么基础_MySQL 基础之 源码 部署
- 数据包注入重放工具aireplay-ng
- Chrome 浏览器提示adobe flash player不是最新版本
- 减小Delphi xe系列生成的exe文件大小
- Swift--控制流与oc不同的地方
- LeetCode 793. 阶乘函数后K个零(二分查找)
- 一个显示器分两个屏幕_桌面改造计划2.0:一个显示器不够那就两个,桌面好物分享...
- bash给脚本加进度条_shell脚本实现多彩进度条
- 数据中心操作人员:艰难地在针对VM构建的基础设施上运行容器
- 程序员输入法_解决Pycharm输入法不跟随光标
- [2019杭电多校第四场][hdu6614]AND Minimum Spanning Tree(贪心)
- URLSession实现iTunes搜索听歌
- java运行库下载_Java运行库下载-jdk1.7 64位下载1.8.0.20 官方最新版-东坡下载
- 推荐20个值得收藏的前端开源项目
- LabVIEW操作者框架(Actor Framework)范例集锦之三:网络搜索范例
- 2019医学电子书下载PDF电子版下载
- linux-CentOS7.6操作系统安装手册
- vue高德地图H5定位及城市选择器控件实现详细教程
- html网页随机一言,PHP简单实现一言 / 随机语录功能
- 养胃有道——送给胃不好的人 (转)
热门文章
- 【AHOI 2016初中组】 自行车比赛 - 贪心
- Angular4.0从入门到实战打造在线竞拍网站学习笔记之四--数据绑定管道
- lucene构建同义词分词器
- 【Java面试题】54 去掉一个Vector集合中重复的元素
- 一个老工程师对理工科学生的忠告
- valgrind——Callgrind检测程序代码的运行时间和调用过程,程序分析性能。
- c语言inline详解
- 为 ASP.NET Datagrid 创建自定义列
- 朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll、Epoll模型处理长连接性能比较
- Ubuntu14.04下配置OpenGL及测试代码