美文网首页
自定义转场动画

自定义转场动画

作者: 洪哥 | 来源:发表于2017-05-09 00:18 被阅读58次

    From和To

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


    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 <UIViewControllerAnimatedTransitioning>
    @end
    
    //AAPLCrossDissolveTransitionAnimator.m
    #import "AAPLCrossDissolveTransitionAnimator.h"
    
    @implementation AAPLCrossDissolveTransitionAnimator
    
    //| ----------------------------------------------------------------------------
    - (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
    {
        return 0.35;
    }
    
    
    //| ----------------------------------------------------------------------------
    - (void)animateTransition:(id<UIViewControllerContextTransitioning>)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),这样可以保证总是获取到最新的、正确的信息。

    转场动画上下文.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

    相关文章

      网友评论

          本文标题:自定义转场动画

          本文链接:https://www.haomeiwen.com/subject/aatmottx.html