源碼下載:源碼
本篇文章是以簡書的自定義轉(zhuǎn)場為原型。先看一下簡書的自定義轉(zhuǎn)場動畫如下:

創(chuàng)建項(xiàng)目
項(xiàng)目中創(chuàng)建兩個(gè)繼承UIViewController的類。分別為ViewController和SecondViewController。
從上圖可以看出,是彈出的模態(tài)視圖。本文自定義present轉(zhuǎn)場的動畫。實(shí)際上就是對
presentViewController和dismissViewControllerAnimated的轉(zhuǎn)場動畫進(jìn)行自定義。
[self presentViewController:secondVC animated:YES completion:NULL];
和
[self dismissViewControllerAnimated:YES completion:NULL];
于是,要?jiǎng)?chuàng)建一個(gè)繼承于NSObject的CustomPresentAnimationCotroller自定義動畫控制器。
最終,項(xiàng)目目錄如下:

分析動畫
-
了解手機(jī)屏幕的坐標(biāo)系。coordinate.jpeg
解釋旋轉(zhuǎn)方向:
x軸和y軸都是沿著手機(jī)屏幕面的垂直方向旋轉(zhuǎn)。但是不同的是:x軸是上下方向,y軸是左右方向。
z軸沿著手機(jī)屏幕面平行方向旋轉(zhuǎn)。
分析present動畫。
是由fromView和toView兩個(gè)視圖的動畫。fromView暫且說是下面的視圖,toView暫且說是上面的視圖。
fromView動畫:
先是沿x,y縮放,透明度減少。然后沿x軸旋轉(zhuǎn)一定角度后向上平移,再縮放一定比例。
toView動畫:
從屏幕底部滑入。分析dismiss動畫
fromView變成上面的視圖,toView則是下面的視圖。
fromView動畫:
從屏幕底部滑出。
toView動畫:
先是沿x,y放大,透明度增大。然后恢復(fù)到原始狀態(tài)。
定義轉(zhuǎn)場動畫
在iOS7后要遵循UIViewControllerTransitioningDelegate協(xié)議。
在本篇文章中使用了以下協(xié)議方法:
//方法1
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
//方法2
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed;
方法1 是在[self presentViewController:secondVC animated:YES completion:NULL];后調(diào)用。
方法2是在[self dismissViewControllerAnimated:YES completion:NULL];后調(diào)用。
在ViewController的按鈕點(diǎn)擊事件后賦予轉(zhuǎn)場代理持有者。轉(zhuǎn)場動畫使用我們自定義的動畫控制器CustomPresentAnimationCotroller實(shí)現(xiàn)。
-(void)presentNext:(UIButton *)sender{
SecondViewController *secondVC =[SecondViewController new];
//blow ios 7.0 can use UIModalPresentationCurrentContext
secondVC.modalPresentationStyle = UIModalPresentationOverCurrentContext;
secondVC.transitioningDelegate = self;
[self presentViewController:secondVC animated:YES completion:NULL];
}
#pragma mark -UIViewControllerTransitioningDelegate
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
CustomPresentAnimationCotroller *presentAnimation = [CustomPresentAnimationCotroller new];
presentAnimation.dismiss = NO;
return presentAnimation;
}
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
CustomPresentAnimationCotroller *presentAnimation = [CustomPresentAnimationCotroller new];
presentAnimation.dismiss = YES;
return presentAnimation;
}
在自定義的動畫控制器CustomPresentAnimationCotroller必須要實(shí)現(xiàn)下面的兩個(gè)轉(zhuǎn)場動畫協(xié)議UIViewControllerAnimatedTransitioning。
//轉(zhuǎn)場動畫時(shí)間
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext {
return 1.0;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *toView = toVC.view;
UIView *fromView = fromVC.view;
if(self.isDismissed){
[self RunDismissAnimation:transitionContext fromVC:fromVC toVC:toVC fromView:fromView toView:toView];
} else {
[self RunPresentAnimation:transitionContext fromVC:fromVC toVC:toVC fromView:fromView toView:toView];
}
}
最后,實(shí)現(xiàn)prensent與dismiss的自定義動畫轉(zhuǎn)場。在下面代碼中小編寫有詳細(xì)的注解。
present動畫:
-(void)RunPresentAnimation:(id<UIViewControllerContextTransitioning>)transitionContext fromVC:(UIViewController *)fromVC toVC:(UIViewController *)toVC fromView:(UIView *)fromView toView:(UIView *)toView {
/*
fromVC UINavigationController 的根視圖控制器---->ViewController
toVC --->SecondViewController
*/
UIView* containerView = [transitionContext containerView];
//獲取fromVC(ViewController)的frame
CGRect frame = [transitionContext initialFrameForViewController:fromVC];
//底部滑進(jìn) 離屏滑入 即y坐標(biāo) 從height --->0
CGRect offScreenFrame = frame;
offScreenFrame.origin.y = offScreenFrame.size.height;
toView.frame = offScreenFrame;
[containerView insertSubview:toView aboveSubview:fromView];
//三維變化
CATransform3D t1 = CATransform3DIdentity;
t1.m34 = 1.0/-1000;
//x y方向各縮放比例為0.95
t1 = CATransform3DScale(t1, 0.95, 0.95, 1);
//x方向旋轉(zhuǎn)15°
t1 = CATransform3DRotate(t1, 15.0f * M_PI/180.0f, 1, 0, 0);
CATransform3D t2 = CATransform3DIdentity;
t2.m34 = 1.0/-1000;
//沿Y方向向上移動
t2 = CATransform3DTranslate(t2, 0, -fromView.frame.size.height*0.08, 0);
//在x y方向各縮放比例為0.8
t2 = CATransform3DScale(t2, 0.8, 0.8, 1);
//UIView關(guān)鍵幀過渡動畫 總的持續(xù)時(shí)間:1.0
[UIView animateKeyframesWithDuration:1.0 delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeCubic animations:^{
//開始時(shí)間:1.0*0.0 持續(xù)時(shí)間:1.0*0.4
[UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:0.4f animations:^{
//執(zhí)行t1動畫 縮放并旋轉(zhuǎn)角度
fromView.layer.transform = t1;
//fromView的透明度
fromView.alpha = 0.6;
}];
//開始時(shí)間:1.0*0.1 持續(xù)時(shí)間:1.0*0.5
[UIView addKeyframeWithRelativeStartTime:0.1f relativeDuration:0.5f animations:^{
//執(zhí)行t2動畫 向上平移和縮放
fromView.layer.transform = t2;
}];
//開始時(shí)間:1.0*0.0 持續(xù)時(shí)間:1.0*1.0
[UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:1.0f animations:^{
//toView向上滑入
toView.frame = frame;
}];
} completion:^(BOOL finished) {
//過渡動畫結(jié)束
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
}
dismiss動畫:
-(void)RunDismissAnimation:(id<UIViewControllerContextTransitioning>)transitionContext fromVC:(UIViewController *)fromVC toVC:(UIViewController *)toVC fromView:(UIView *)fromView toView:(UIView *)toView {
CGRect frame = [transitionContext initialFrameForViewController:fromVC];
toView.frame = frame;
CGRect frameOffScreen = frame;
frameOffScreen.origin.y = frame.size.height;
CATransform3D t1 = CATransform3DIdentity;
t1.m34 = 1.0/-1000;
t1 = CATransform3DScale(t1, 0.95, 0.95, 1);
t1 = CATransform3DRotate(t1, 15.0f * M_PI/180.0f, 1, 0, 0);
//關(guān)鍵幀過渡動畫
[UIView animateKeyframesWithDuration:1.0 delay:0 options:UIViewKeyframeAnimationOptionCalculationModeCubic animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:1.0f animations:^{
//滑出屏幕
fromView.frame = frameOffScreen;
}];
[UIView addKeyframeWithRelativeStartTime:0.35f relativeDuration:0.35f animations:^{
//執(zhí)行t1,沿著x,y放大,沿x旋轉(zhuǎn)
toView.layer.transform = t1;
//透明度變?yōu)?.0
toView.alpha = 1.0;
}];
[UIView addKeyframeWithRelativeStartTime:0.75f relativeDuration:0.25f animations:^{
//還原3D狀態(tài)
toView.layer.transform = CATransform3DIdentity;
}];
} completion:^(BOOL finished) {
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
}];
最終效果圖:

本篇詳解到此結(jié)束。如有疑問請留言給小編,小編給大家做解答。
問題1:自定義動畫轉(zhuǎn)場后,調(diào)用dismissViewControllerAnimated,上一級的ViewController不會觸發(fā)viewWillAppear。
解決方法:
第一步在自定義轉(zhuǎn)場中實(shí)現(xiàn)改協(xié)議方法。
-(void)animationEnded:(BOOL)transitionCompleted{
if (!transitionCompleted) {
_toVC.view.transform = CGAffineTransformIdentity;
}
}
第二步下面方法加入一行代碼:
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext {
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
_toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIView *toView = _toVC.view;
UIView *fromView = fromVC.view;
//toVC 調(diào)用viewWillAppear
[_toVC beginAppearanceTransition:YES animated:YES];
if(self.isDismissed){
[self RunDismissAnimation:transitionContext fromVC:fromVC toVC:_toVC fromView:fromView toView:toView];
} else {
[self RunPresentAnimation:transitionContext fromVC:fromVC toVC:_toVC fromView:fromView toView:toView];
}
// 調(diào)用此方法
// fromVC 調(diào)用viewDidDisappear
[fromVC beginAppearanceTransition:NO animated:YES];
點(diǎn)擊此處源碼下載:源碼
其實(shí)自定義動畫轉(zhuǎn)場并不是特別難,需要自己動手做一次。
