自定义Push和Pop过渡动画

来源:互联网 发布:the game awards知乎 编辑:程序博客网 时间:2024/06/08 20:04

一、效果和源码

本文介绍如何实现一个NavigationController的自定义Push和Pop过渡动画,运行效果如下:



源码:https://github.com/dolacmeng/TransitionDemo 或
http://download.csdn.net/detail/dolacmeng/9572384

二、准备工作

首先,新建两个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中,在屏幕左边缘往中间水平滑动,将可以看到过渡进度随着我们的手势而不同。


参考文献:
http://dativestudios.com/blog/2013/09/29/interactive-transitions/
《iOS Animation by Tutorials 2.0》


觉得上面过程太复杂?现在可以简单几行代码实现神奇移动过渡啦,请看我的新文章哦:
http://blog.csdn.net/dolacmeng/article/details/56485140


0 0
原创粉丝点击