iOS7 interactive transitions(视图控制器的过渡动画 )

原文地址:http://dativestudios.com/blog/2013/09/29/interactive-transitions/


iOS7 引入了一种新的视图控制器过渡的方式,通过这篇文章和项目源码看看怎么使用 UINavigationController实现自定义的transition.

在这个demo中我们会在两个view controller之间添加一个custom transition,DSLFirstViewController 作为一个collectionview呈现Things实例,每个cell有一个image和label,DSLSecondViewController展示单个thing对象,同样拥有一个image和label。在过渡过程中我们要实现的就是把cell的image view 过渡到 第二个控制器的 imageView。

The Xcode project for this article is available on GitHub.

Implementing a custom transition

这个transition由一个遵守<UIViewControllerAnimatedTransitioning>协议的NSObject的对象控制,在这个transition object中,主要有两个要实现的委托方法 animateTransition:和transitionDuration:

transitionDuration:确定过渡的时间

- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
    return 0.3;
}
animateTransition:方法实现两个view之间过渡时的主要逻辑,这个方法传递给了几个我们可以在transition过程使用的参数

  • viewControllerForKey: gives us access to the two view controllers we‘re transitioning between
  • containerView property gives us the containing view of both view controllers
  • initialFrameForViewController: and finalFrameForViewController give us the start and end position of each view controller‘s view
看看animateTransition的实现,第一件事就是获取view controllers 和 containing view的指针

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

    UIView *containerView = [transitionContext containerView];
接下来获取用来过渡的cell,并对cell的imageView进行快照(snapshot),这个snapshot就是在过渡过程中要实现动画的对象

主要思路就是:隐藏cell.imageView这样给人的错觉是cell.imageView在动画而不是这个snapshot

   // Get a snapshot of the thing cell we're transitioning from
    DSLThingCell *cell = (DSLThingCell*)[fromViewController.collectionView cellForItemAtIndexPath:[[fromViewController.collectionView indexPathsForSelectedItems] firstObject]];
    UIView *cellImageSnapshot = [cell.imageView snapshotView];
    cellImageSnapshot.frame = [containerView convertRect:cell.imageView.frame fromView:cell.imageView.superview];
    cell.imageView.hidden = YES;
把second view controller‘s view放到相应位置使它透明,之后在过渡过程来把它渐显出来

 // Setup the initial view states
    toViewController.view.frame = [transitionContext finalFrameForViewController:toViewController];
    toViewController.view.alpha = 0;
    toViewController.imageView.hidden = YES;

    [containerView addSubview:toViewController.view];
    [containerView addSubview:cellImageSnapshot];
现在开始给views 动画,移动image snapshot,在第二个controller中逐渐显示出来,最后在动画的completion block中移除snapshot,恢复透明度就行了

    [UIView animateWithDuration:duration animations:^{
        // Fade in the second view controller's view
        toViewController.view.alpha = 1.0;

        // Move the cell snapshot so it's over the second view controller's image view
        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];

        // Declare that we've finished
        [transitionContext completeTransition:!transitionContext.transitionWasCancelled];
    }];
}

Using the custom transition

到此我们自定义的 transition object就实现了,接下来就是告诉UINavigationController来使用它,当一个viewcontroller从navigation的stack中push或者pop off的时候,UINavigatonDelegate的委托方法就需要寻求这个 transition object类了

(翻译的好吃力啊  )

The first thing to do is to specify that our view controller conforms to the UINavigationControllerDelegate protocol

@interface DSLFirstViewController ()<UINavigationControllerDelegate>

We also need to make our view controller it‘s navigation controller‘s delegate. A good spot to do this is in viewDidAppear:.

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

    // Set outself as the navigation controller‘s delegate so we‘re asked for a transitioning object
    self.navigationController.delegate = self;
}

We should also stop being the navigation control‘s delegate when the view controller is disappearing

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

    // Stop being the navigation controller‘s delegate
    if (self.navigationController.delegate == self) {
        self.navigationController.delegate = nil;
    }
}

Now our view controller is the navigation controller‘s delegate, we can implement the following longwinded UINavigationControllerDelegate method

- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                  animationControllerForOperation:(UINavigationControllerOperation)operation
                                               fromViewController:(UIViewController *)fromVC
                                                 toViewController:(UIViewController *)toVC {
    // Check if we‘re transitioning from this view controller to a DSLSecondViewController
    if (fromVC == self && [toVC isKindOfClass:[DSLSecondViewController class]]) {
        return [[DSLTransitionFromFirstToSecond alloc] init];
    }
    else {
        return nil;
    }
}

Making transitions interactive

UIScreenEdgePanGestureRecognizer和UIPercentDrivenInteractiveTransition的理解
- (void)viewDidLoad {
    [super viewDidLoad];

    ...

    UIScreenEdgePanGestureRecognizer *popRecognizer = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePopRecognizer:)];
    popRecognizer.edges = UIRectEdgeLeft;
    [self.view addGestureRecognizer:popRecognizer];
}
- (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;
    }
}

Now we‘re creating and updating a UIPercentDrivenInteractiveTransition object, we need to tell the navigation controller to use it. We do this by implementing another new UINavigationControllerDelegate method:

- (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;
    }
}

Summary

Hopefully this post and the example project will help you to understand the steps needed to create your own custom transitions and interactions. A sample Xcode project is available on GitHub.


附上几个相关例子
http://onevcat.com/2013/10/vc-transition-in-ios7/
http://blog.kingiol.com/blog/2014/01/18/ios7-day-by-day-day14-interactive-view-controller-transitions/
http://www.shinobicontrols.com/blog/posts/2013/10/03/ios7-day-by-day-day-10-custom-uiviewcontroller-transitions

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。