先上效果图:
细心的小伙伴会发现,现在 网易云音乐的广告页 以及 微信相册中点击查看评论 都用了该转场动画。作为一名 iOS 程序员,本能的想去实现它。
说明:本篇博文参考了 onevcat 的 WWDC 2013 Session笔记 以及 GitHub 上的开源项目。不打算先理论了,直接拿 demo 项目开撸。
项目结构
很显然,有两个 ViewController,在本例中分别是 MainViewController(下文简称 mainVC) 和 ForwardViewController(简称 forwardVC)。
首先,我们让 mainVC 声明 UIViewControllerTransitioningDelegate 协议,并实现 - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
方法。
该方法需要你返回一个自定义的动画,该动画类型为 NSObject ,并需要实现 UIViewControllerAnimatedTransitioning 协议。
因此,接下来我们需要实现这样一个实现 UIViewControllerAnimatedTransitioning 协议的 object,不妨取名为 NeteaseTransitionAnimation。
实现 NeteaseTransitionAnimation
在本例中,我们着重看 UIViewControllerAnimatedTransitioning 协议中的两个方法,一是 - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
,第二个是 - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
。
第一个方法传入动画的时间长度,在这里我们设置为 0.8 秒,我们主要在第二个方法中设定具体的动画效果。
第二个方法中有一个很重要的 UIViewControllerContextTransitioning 类型的上下文对象 transitionContext,通过该对象我们可以获得切出以及切入的 VC 从而设置动画的具体参数等。不多说,接下来直接上具体实现步骤及相应代码。
获得切出和切入的 ViewController
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
获得 containerView 并将待切入的 view 加入其中
UIView *containerView = [transitionContext containerView];
[containerView addSubview:toVC.view];
有人问,containerView 是用来干嘛的。官方定义: * The view that acts as the superview for the views involved in the transition. * 。并且,它会自动的将当前所在的 viewController 的 view 加入其中。
使用 CATransform3D 实现动画过程
这步应该算是本篇中最核心的一步。关于涉及到的动画知识,建议先看iOS 核心动画高级技巧中的 3D 变换这一章,在本篇博文中就不具体谈了。
代码:
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -0.002; // 设置透视效果,一般设为 -0.002
[containerView.layer setSublayerTransform:transform]; // 将动画影响到父视图下的所有子视图
CGRect initialFrame = [transitionContext initialFrameForViewController:fromVC];
toVC.view.frame = initialFrame; // 给 toVC 设置初始 frame
让我们重新看一下开头的效果图,可以将整个动画过程分成 3 个阶段。(假设我们将一个周期的总时间设为 t,并规定逆时针转动为正,顺时针转动为负,逆顺指针均从视角的正上往正下看)
0 阶段
fromVC 初始在原位,而 toVC 初始化在 pi / 2 的位置。见图:
具体代码:
toVC.view.layer.transform = CATransform3DMakeRotation(M_PI_2, 0.0, 1.0, 0.0);
0 ~ t / 2 阶段
fromVC 顺时针从 0 度转到 - pi / 2 的位置,toVC 在 pi / 2 位置保持不动。(这时在视觉上二者重合)
t / 2 ~ t 阶段
toVC 从初始的 pi / 2 位置顺时针转到 0 位置。
0 ~ pi 阶段的具体代码:
[UIView animateKeyframesWithDuration:duration delay:0.0 options:0 animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0
relativeDuration:0.5
animations:^{
fromVC.view.layer.transform = CATransform3DMakeRotation(- M_PI_2, 0.0, 1.0, 0.0);
}];
[UIView addKeyframeWithRelativeStartTime:0.5
relativeDuration:0.5
animations:^{
toVC.view.layer.transform = CATransform3DMakeRotation(0.0, 0.0, 1.0, 0.0);
}];
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
值得提一下 [transitionContext completeTransition:![transitionContext transitionWasCancelled]];
,如果没有取消动画的话,即为 [transitionContext completeTransition: YES]。在该方法中,* UIKit will ensure the final state is consistent and remove the fromView from the container. * ,即,在我们这里,该方法将会移除 fromVC.view 。
结尾
该动画源码已上传 GitHub,链接戳此。有问题可以提 issue 或者直接在本博文下方的评论框中进行评论。
其实该动画并不难,建议对着源码多比较,并按 option + 函数名 查看官方定义,会很有收获的。
参考链接也不另给了,基本都是 Apple 官方的文档,没有什么资料比官方文档更有说服力了。
联系我
- 微博:@CaiYue_
- 简书:蔡越
- 邮箱:yuecai.nju@gmail.com