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

源碼下載:源碼

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


JianshuTransition.gif

創(chuàng)建項目

  1. 項目中創(chuàng)建兩個繼承UIViewController的類蚤蔓。分別為ViewController和SecondViewController赋秀。

  2. 從上圖可以看出,是彈出的模態(tài)視圖。本文自定義present轉場的動畫隘梨。實際上就是對presentViewControllerdismissViewControllerAnimated的轉場動畫進行自定義蚀腿。

[self presentViewController:secondVC animated:YES completion:NULL];

[self dismissViewControllerAnimated:YES completion:NULL];

于是嘴瓤,要創(chuàng)建一個繼承于NSObject的CustomPresentAnimationCotroller自定義動畫控制器。

最終莉钙,項目目錄如下:


project.png

分析動畫

  1. 了解手機屏幕的坐標系廓脆。
    coordinate.jpeg

解釋旋轉方向:
x軸和y軸都是沿著手機屏幕面的垂直方向旋轉。但是不同的是:x軸是上下方向磁玉,y軸是左右方向停忿。
z軸沿著手機屏幕面平行方向旋轉。

  1. 分析present動畫蚊伞。
    是由fromViewtoView兩個視圖的動畫席赂。fromView暫且說是下面的視圖,toView暫且說是上面的視圖时迫。
    fromView動畫:
    先是沿x,y縮放颅停,透明度減少。然后沿x軸旋轉一定角度后向上平移掠拳,再縮放一定比例癞揉。
    toView動畫:
    從屏幕底部滑入。

  2. 分析dismiss動畫
    fromView變成上面的視圖溺欧,toView則是下面的視圖喊熟。
    fromView動畫:
    從屏幕底部滑出。
    toView動畫:
    先是沿x,y放大姐刁,透明度增大逊移。然后恢復到原始狀態(tài)。

定義轉場動畫

在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];后調用胳泉。
方法2是在[self dismissViewControllerAnimated:YES completion:NULL];后調用拐叉。

在ViewController的按鈕點擊事件后賦予轉場代理持有者。轉場動畫使用我們自定義的動畫控制器CustomPresentAnimationCotroller實現(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必須要實現(xiàn)下面的兩個轉場動畫協(xié)議UIViewControllerAnimatedTransitioning凤瘦。

//轉場動畫時間
- (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];
    }
}

最后,實現(xiàn)prensent與dismiss的自定義動畫轉場案铺。在下面代碼中小編寫有詳細的注解蔬芥。

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];
    //底部滑進 離屏滑入 即y坐標 從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方向旋轉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關鍵幀過渡動畫 總的持續(xù)時間:1.0
    [UIView animateKeyframesWithDuration:1.0 delay:0.0 options:UIViewKeyframeAnimationOptionCalculationModeCubic animations:^{
        
        //開始時間:1.0*0.0 持續(xù)時間:1.0*0.4
        [UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:0.4f animations:^{
            //執(zhí)行t1動畫 縮放并旋轉角度
            fromView.layer.transform = t1;
            //fromView的透明度
            fromView.alpha = 0.6;
        }];
        //開始時間:1.0*0.1 持續(xù)時間:1.0*0.5
        [UIView addKeyframeWithRelativeStartTime:0.1f relativeDuration:0.5f animations:^{
            //執(zhí)行t2動畫 向上平移和縮放
            fromView.layer.transform = t2;
        }];
        //開始時間:1.0*0.0 持續(xù)時間:1.0*1.0
        [UIView addKeyframeWithRelativeStartTime:0.0f relativeDuration:1.0f animations:^{
            //toView向上滑入
            toView.frame = frame;
        }];
        
    } completion:^(BOOL finished) {
        //過渡動畫結束
        [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);
    
    //關鍵幀過渡動畫
    [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旋轉
            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

本篇詳解到此結束控汉。如有疑問請留言給小編笔诵,小編給大家做解答。

問題1:自定義動畫轉場后姑子,調用dismissViewControllerAnimated乎婿,上一級的ViewController不會觸發(fā)viewWillAppear。

解決方法:
第一步在自定義轉場中實現(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 調用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];
    }

//    調用此方法
//    fromVC 調用viewDidDisappear
    [fromVC beginAppearanceTransition:NO animated:YES];

點擊此處源碼下載:源碼
其實自定義動畫轉場并不是特別難谢翎,需要自己動手做一次。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末沐旨,一起剝皮案震驚了整個濱河市森逮,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌磁携,老刑警劉巖褒侧,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谊迄,居然都是意外死亡璃搜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進店門鳞上,熙熙樓的掌柜王于貴愁眉苦臉地迎上來这吻,“玉大人,你說我怎么就攤上這事篙议⊥倥矗” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵鬼贱,是天一觀的道長移怯。 經(jīng)常有香客問我,道長这难,這世上最難降的妖魔是什么舟误? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮姻乓,結果婚禮上嵌溢,老公的妹妹穿的比我還像新娘眯牧。我一直安慰自己,他們只是感情好赖草,可當我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布学少。 她就那樣靜靜地躺著,像睡著了一般秧骑。 火紅的嫁衣襯著肌膚如雪版确。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天乎折,我揣著相機與錄音绒疗,去河邊找鬼。 笑死骂澄,一個胖子當著我的面吹牛吓蘑,可吹牛的內容都是我干的。 我是一名探鬼主播酗洒,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼枷遂!你這毒婦竟也來了樱衷?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤酒唉,失蹤者是張志新(化名)和其女友劉穎矩桂,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體痪伦,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡侄榴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了网沾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片癞蚕。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖辉哥,靈堂內的尸體忽然破棺而出桦山,到底是詐尸還是另有隱情,我是刑警寧澤醋旦,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布恒水,位于F島的核電站,受9級特大地震影響饲齐,放射性物質發(fā)生泄漏钉凌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一捂人、第九天 我趴在偏房一處隱蔽的房頂上張望御雕。 院中可真熱鬧矢沿,春花似錦、人聲如沸饮笛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽福青。三九已至摄狱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間无午,已是汗流浹背媒役。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留宪迟,地道東北人酣衷。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像次泽,于是被迫代替她去往敵國和親穿仪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內容