自定义Push和Pop过渡动画
来源:互联网 发布:the game awards知乎 编辑:程序博客网 时间:2024/06/08 20:04
一、效果和源码
本文介绍如何实现一个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 *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(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 view CGFloat 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 controller self.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 transition if (progress > 0.5) { [self.interactivePopTransition finishInteractiveTransition]; } else { [self.interactivePopTransition cancelInteractiveTransition]; } self.interactivePopTransition = nil; }}
现在我们创建和更新UIPercentDrivenInteractiveTransition实例,我们还需要告诉navigation controller来使用它,在SecondViewController中添加:
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController { // Check if this is for our custom transition if ([animationController isKindOfClass:[DSLTransitionFromSecondToFirst class]]) { return self.interactivePopTransition; } else { return nil; }}
运行Demo,在secondViewController中,在屏幕左边缘往中间水平滑动,将可以看到过渡进度随着我们的手势而不同。
- 自定义Push和Pop过渡动画
- 自定义NavigationController 的Push 和 Pop过渡动画
- UINavigationController自定义,push和pop动画
- UINavigationController自定义,push和pop动画
- IOS 自定义push和pop动画
- NavigationController 自定义pop和push动画
- pop push UIViewController 手势过渡动画
- 实例 关于自定义Push Pop过渡
- iOS7自定义视图控制器过渡3-关于Push和Pop的过渡
- 自定义PUSH POP跳转动画
- Swift基础之自定义PUSH和POP跳转动画
- iOS 自定义动画 push/pop动画
- ios开发 自定义带弧度的UITabBar,保留系统原有push和pop过渡效果
- 自定义导航栏pop动画(push同理)
- navigationController push和pop界面切换动画
- 定义NavigationController pop 和push的动画
- swift详解之二十七------------自定义UINavigationController的push和pop动画
- 【iOS学习笔记 15-11-06】简单自定义navigationcontroller push和pop动画效果
- 大数据:Spark性能优化指南 高级篇
- HDU 4277 USACO ORZ DFS
- 【zTree】简单实例与异步加载实例
- 学习Java的第一天
- spark shell 启动出错问题
- 自定义Push和Pop过渡动画
- java泛型通配符示例
- 泛型的嵌套设置
- C++ virtual关键字深入理解
- Win10Matlab7Runtime Error
- hdu 4751 Divide Groups bfs 搜索 解题报告
- 传球游戏_vijos1485
- 构造方法中super()的作用
- Codeforces 689C Mike and Chocolate Thieves (二分)