自定义转场动画
From和To
fromView表示当前视图,toView表示要跳转到的视图。
如果是从A视图控制器present到B,则A是from,B是to。从B视图控制器dismiss到A时,B变成了from,A是to。用一张图表示:
![](https://img.haomeiwen.com/i1293704/715f0d234b25a027.png)
Presented和Presenting
不受present或dismiss的影响,如果是从A视图控制器present到B,那么A总是B的presentingViewController,B总是A的presentedViewController。
modalPresentationStyle
枚举类型,表示present时动画的类型。其中可以自定义动画效果的只有两种:FullScreen和Custom,两者的区别在于FullScreen会移除fromView,而Custom不会。
基于block的动画
最简单的转场动画是使用transitionFromViewController方法:
![](https://img.haomeiwen.com/i1293704/c28b5a09a3b45c5b.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),这样可以保证总是获取到最新的、正确的信息。
![](https://img.haomeiwen.com/i1293704/9a77b98d42c7a27f.png)
获取到足够信息后,我们调用UIView.animateWithDuration方法把动画交给Core Animation处理。千万不要忘记在动画调用结束后,执行completeTransition方法。
- present时,要把toView加入到container的视图层级。
- dismiss时,要把fromView从container的视图层级中移除。
交互式(Interactive)转场动画
刚刚我们说到,设置了toViewController
的transitioningDelegate
属性并且present
时,UIKit会从代理处获取animator,其实这里还有一个细节:UIKit还会调用代理的interactionControllerForPresentation:
方法来获取交互式控制器,如果得到了nil则执行非交互式动画,这就回到了上一节的内容。
如果获取到了不是nil
的对象,那么UIKit不会调用animator
的animateTransition
方法,而是调用交互式控制器(还记得前面介绍动画代理的示意图么,交互式动画控制器和animator是平级关系)的startInteractiveTransition:
方法。
所谓的交互式动画,通常是基于手势驱动,产生一个动画完成的百分比来控制动画效果。整个动画不再是一次性、连贯的完成,而是在任何时候都可以改变百分比甚至取消。这需要一个实现了UIPercentDrivenInteractiveTransition
协议的交互式动画控制器和animator
协同工作。这看上去是一个非常复杂的任务,但UIKit已经封装了足够多细节,我们只需要在交互式动画控制器和中定义一个时间处理函数(比如处理滑动手势),然后在接收到新的事件时,计算动画完成的百分比并且调用updateInteractiveTransition
来更新动画进度即可。
edit -> http://www.jianshu.com/p/ea0132738057