自定义转场动画

From和To

fromView表示当前视图,toView表示要跳转到的视图。
如果是从A视图控制器present到B,则A是from,B是to。从B视图控制器dismiss到A时,B变成了from,A是to。用一张图表示:


自定义转场动画_第1张图片
From和To.png

Presented和Presenting

不受present或dismiss的影响,如果是从A视图控制器present到B,那么A总是B的presentingViewController,B总是A的presentedViewController。

modalPresentationStyle

枚举类型,表示present时动画的类型。其中可以自定义动画效果的只有两种:FullScreen和Custom,两者的区别在于FullScreen会移除fromView,而Custom不会。

基于block的动画

最简单的转场动画是使用transitionFromViewController方法:

传统的转场动画实现.png

6个参数,前两个表示从哪个VC开始,跳转到哪个VC,中间两个参数表示动画的时间和选项。最后两个参数表示动画的具体实现细节和回调闭包。

自定义present转场动画

转场动画代理,它是一个实现了UIViewControllerTransitioningDelegate协议的对象。

Animator:

它是实现了UIViewControllerAnimatedTransitioning协议的对象,用于控制动画的持续时间和动画展示逻辑,代理可以为present和dismiss过程分别提供Animator,也可以提供同一个Animator。

交互式Animator:和Animator类似,不过它是交互式的,后面会有详细介绍

Presentation控制器:

  • 创建动画代理
  • 设置B的transitioningDelegate为步骤1中创建的代理对象
  • 调用presentViewController:animated:completion:并把参数animated设置为true
  • 系统会找到代理中提供的Animator,由Animator负责动画逻辑
//AAPLCrossDissolveTransitionAnimator.h
@import UIKit;

@interface AAPLCrossDissolveTransitionAnimator : NSObject 
@end
//AAPLCrossDissolveTransitionAnimator.m
#import "AAPLCrossDissolveTransitionAnimator.h"

@implementation AAPLCrossDissolveTransitionAnimator

//| ----------------------------------------------------------------------------
- (NSTimeInterval)transitionDuration:(id)transitionContext
{
    return 0.35;
}


//| ----------------------------------------------------------------------------
- (void)animateTransition:(id)transitionContext
{
    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    UIView *containerView = transitionContext.containerView;
    
    // For a Presentation:
    //      fromView = The presenting view.
    //      toView   = The presented view.
    // For a Dismissal:
    //      fromView = The presented view.
    //      toView   = The presenting view.
    UIView *fromView;
    UIView *toView;
    
    // In iOS 8, the viewForKey: method was introduced to get views that the
    // animator manipulates.  This method should be preferred over accessing
    // the view of the fromViewController/toViewController directly.
    // It may return nil whenever the animator should not touch the view
    // (based on the presentation style of the incoming view controller).
    // It may also return a different view for the animator to animate.
    //
    // Imagine that you are implementing a presentation similar to form sheet.
    // In this case you would want to add some shadow or decoration around the
    // presented view controller's view. The animator will animate the
    // decoration view instead and the presented view controller's view will
    // be a child of the decoration view.
    if ([transitionContext respondsToSelector:@selector(viewForKey:)]) {
        fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
        toView = [transitionContext viewForKey:UITransitionContextToViewKey];
    } else {
        fromView = fromViewController.view;
        toView = toViewController.view;
    }
    
    fromView.frame = [transitionContext initialFrameForViewController:fromViewController];
    toView.frame = [transitionContext finalFrameForViewController:toViewController];
    
    fromView.alpha = 1.0f;
    toView.alpha = 0.0f;
    
    // We are responsible for adding the incoming view to the containerView
    // for the presentation/dismissal.
    [containerView addSubview:toView];
    
    NSTimeInterval transitionDuration = [self transitionDuration:transitionContext];
    
    [UIView animateWithDuration:transitionDuration animations:^{
        fromView.alpha = 0.0f;
        toView.alpha = 1.0;
    } completion:^(BOOL finished) {
        // When we complete, tell the transition context
        // passing along the BOOL that indicates whether the transition
        // finished or not.
        BOOL wasCancelled = [transitionContext transitionWasCancelled];
        [transitionContext completeTransition:!wasCancelled];
    }];
}

@end

animateTransition方法的核心则是从转场动画上下文获取必要的信息以完成动画。上下文是一个实现了UIViewControllerContextTransitioning的对象,它的作用在于为animateTransition方法提供必备的信息。您不应该缓存任何关于动画的信息,而是应该总是从转场动画上下文中获取(比如fromView和toView),这样可以保证总是获取到最新的、正确的信息。

自定义转场动画_第2张图片
转场动画上下文.png

获取到足够信息后,我们调用UIView.animateWithDuration方法把动画交给Core Animation处理。千万不要忘记在动画调用结束后,执行completeTransition方法。

  • present时,要把toView加入到container的视图层级。
  • dismiss时,要把fromView从container的视图层级中移除。

交互式(Interactive)转场动画

刚刚我们说到,设置了toViewControllertransitioningDelegate属性并且present时,UIKit会从代理处获取animator,其实这里还有一个细节:UIKit还会调用代理的interactionControllerForPresentation:方法来获取交互式控制器,如果得到了nil则执行非交互式动画,这就回到了上一节的内容。

如果获取到了不是nil的对象,那么UIKit不会调用animatoranimateTransition方法,而是调用交互式控制器(还记得前面介绍动画代理的示意图么,交互式动画控制器和animator是平级关系)的startInteractiveTransition:方法。

所谓的交互式动画,通常是基于手势驱动,产生一个动画完成的百分比来控制动画效果。整个动画不再是一次性、连贯的完成,而是在任何时候都可以改变百分比甚至取消。这需要一个实现了UIPercentDrivenInteractiveTransition协议的交互式动画控制器和animator协同工作。这看上去是一个非常复杂的任务,但UIKit已经封装了足够多细节,我们只需要在交互式动画控制器和中定义一个时间处理函数(比如处理滑动手势),然后在接收到新的事件时,计算动画完成的百分比并且调用updateInteractiveTransition来更新动画进度即可。

edit -> http://www.jianshu.com/p/ea0132738057

NOT END

你可能感兴趣的:(自定义转场动画)