干貨系列之實(shí)戰(zhàn)詳解自定義轉(zhuǎn)場動畫

源碼下載:源碼

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


JianshuTransition.gif

創(chuàng)建項(xiàng)目

  1. 項(xiàng)目中創(chuàng)建兩個(gè)繼承UIViewController的類。分別為ViewController和SecondViewController。

  2. 從上圖可以看出,是彈出的模態(tài)視圖。本文自定義present轉(zhuǎn)場的動畫。實(shí)際上就是對presentViewControllerdismissViewControllerAnimated的轉(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)目目錄如下:


project.png

分析動畫

  1. 了解手機(jī)屏幕的坐標(biāo)系。
    coordinate.jpeg

解釋旋轉(zhuǎn)方向:
x軸和y軸都是沿著手機(jī)屏幕面的垂直方向旋轉(zhuǎn)。但是不同的是:x軸是上下方向,y軸是左右方向。
z軸沿著手機(jī)屏幕面平行方向旋轉(zhuǎn)。

  1. 分析present動畫。
    是由fromViewtoView兩個(gè)視圖的動畫。fromView暫且說是下面的視圖,toView暫且說是上面的視圖。
    fromView動畫:
    先是沿x,y縮放,透明度減少。然后沿x軸旋轉(zhuǎn)一定角度后向上平移,再縮放一定比例。
    toView動畫:
    從屏幕底部滑入。

  2. 分析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]];
    }];

最終效果圖:

finishTransition.gif

本篇詳解到此結(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)場并不是特別難,需要自己動手做一次。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容