iOS学习之——自定义过渡动画的实现和使用

一、实现自定义过渡

过渡是由使用了 UIViewControllerAnimatedTransitioning 协议的对象来实现的。我们现在新建一个继承自 NSObject 的类,取名 DSLTransitionFromFirstToSecond。将上面提到的协议加入该类,然后就可以使用他来实现我们的两个类的过渡效果了。

在这个对象中,有两个方法需要实现:animateTransition: 和 transitionDuration:。后者相当直观,就是这个过渡的持续时间,我们只要简单返回一个 NSTimeInterval 值就行。

- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext { 
    return 0.3; 
} 

animateTransition: 方法是定义两个 ViewController 之间过渡效果的地方。这个方法会传递给我们一个参数,该参数可以让我们访问一些实现过渡所必须的对象。

- viewControllerForKey://我们可以通过他访问过渡的两个 ViewController。 - containerView://两个 ViewController 的 containerView。 - initialFrameForViewController 和 finalFrameForViewController //是过渡开始和结束时每个 ViewController 的 frame。

现在我们开始这个方法的具体实现。首先我们需要得到过渡前后两个 ViewController 以及他们的 containerView 的指针。

- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext { DSLFirstViewController *fromViewController = (DSLFirstViewController*)[transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]; DSLSecondViewController *toViewController = (DSLSecondViewController*)[transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]; 
UIView *containerView = [transitionContext containerView]; 

接下来,获得我们需要过渡的 Cell,并且对它上面的 imageView 截图。这个截图就会用在我们的过渡效果中。同时,我们将这个 imageView 本身隐藏,从而让用户以为是 imageView 在移动的。

// 获得cell上imageView的截图 
    DSLThingCell *cell = (DSLThingCell*)[fromViewController.collectionView cellForItemAtIndexPath:[[fromViewController.collectionView indexPathsForSelectedItems] firstObject]]; 
    //生成快照(用于我们的过渡效果中)
    //设置No会立即生成快照,并不会调用重新设置颜色的方法
    UIView *cellImageSnapshot = [cell.imageView snapshotViewAfterScreenUpdates:NO];
    cellImageSnapshot.frame = [containerView convertRect:cell.imageView.frame fromView:cell.imageView.superview]; 
    cell.imageView.hidden = YES; 

然后,我们对第二个 viewController 进行设置,将它的放到过渡后的位置,但让他完全透明,我们会在过渡时给它一个淡入的效果。

// 初始化一开始的状态 
    toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController]; 
    toViewController.view.alpha = 0; 
    toViewController.imageView.hidden = YES; 

    [containerView addSubview:toViewController.view]; 
    [containerView addSubview:cellImageSnapshot]; 

现在来做 view 的动画,移动之前生成的 imageView 的截图,淡入第二个 viewController 的 view。在动画结束后,移除 imageView 的截图,让第二个 view 完全呈现。

[UIView animateWithDuration:duration animations:^{ 
        // 淡入第二个viewController的view 
        toViewController.view.alpha = 1.0; 

        // 将截图放到第二个viewController的imageView上 

        // 将rect从view中转换到当前视图中,返回在当前视图中的rect
        CGRect frame = [containerView convertRect:toViewController.imageView.frame fromView:toViewController.view]; 
        cellImageSnapshot.frame = frame; 
    } completion:^(BOOL finished) { 
        // Clean up 
        toViewController.imageView.hidden = NO; 
        cell.hidden = NO; 
        [cellImageSnapshot removeFromSuperview]; 

        // 声明过渡结束 
        [transitionContext completeTransition:!transitionContext.transitionWasCancelled]; 
    }]; 
} 

记住,一定别忘了在过渡结束时调用 completeTransition: 这个方法。

二、使用自定义过渡

到目前为止,我们实现了自定义过渡对象,不过我们并没有告知 UINavigationController 去使用它。接下来,将介绍我们如何做到这一点。

当一个新的 viewController 被推入或者弹出它的导航堆,它将询问它的代理,是否有一个使用了 UIViewCOntrollerAnimatedTransitioning 协议的对象,我们现在要做的,就是提供这个对象使得过渡能够展现。

首先是把 UINavigationControllerDelegate 协议加入到 DSLFirstViewController 中去。

@interface DSLFirstViewController ()<UINavigationControllerDelegate> 

我们还需要给 navigationController 的 delegate 赋值。一个比较理想的地方是在 viewDidAppear:。

- (void)viewDidAppear:(BOOL)animated { 
    [super viewDidAppear:animated]; 

    // 让我自己变成navigationController的delegate 
    self.navigationController.delegate = self; 
} 

别忘了在 view 消失时,把 navigationController 的 delegate 去除。

- (void)viewWillDisappear:(BOOL)animated { 
    [super viewWillDisappear:animated]; 

    // 我不再是 navigationController 的代理啦 
    if (self.navigationController.delegate == self) { 
        self.navigationController.delegate = nil; 
    } 
} 

现在我们可以开始实现这个长长名字的 UINavigationControllerDelegate 的方法。

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController 
                                  animationControllerForOperation:(UINavigationControllerOperation)operation 
                                               fromViewController:(UIViewController *)fromVC 
                                                 toViewController:(UIViewController *)toVC { 
    // 检查一下是不是过渡到DSLSecondViewController 
    if (fromVC == self && [toVC isKindOfClass:[DSLSecondViewController class]]) { 
        return [[DSLTransitionFromFirstToSecond alloc] init]; 
    } 
    else { 
        return nil; 
    } 
} 

That’s it. 当第二个 viewController 被推入进来时,navigationController 将使用我们自定义的过渡。

要实现弹回时的过渡效果,还是一样的方法,实现一个新的 DSLTransitionFromSecondToFirst 类用来过渡即可。

原文:

http://www.cocoachina.com/industry/20140623/8918.html
项目发布在GitHub中

你可能感兴趣的:(ios,动画,diy,过渡)