iOS UIKit:viewController之動畫(5)

      當彈出一個view controller時,UIKit提供了一些標準轉換動畫,而且也支持用戶自定義的動畫效果。 app

1 UIView動畫 ide

UIView是自帶動畫實現功能,其中有兩種方式實現: 函數

       1) animateWithDuration系列方法 測試

       2) transitionFromViewController方法 動畫

1.1 animateWithDuration ui

      只要在該方法的animations block中修改UIView的動畫屬性,那麼便可實現動畫變換,因此爲container viewController中實現不一樣view controller之間的動畫切換,只要改變了UIView的動畫屬性便可以實現變換。 spa

      如在調用viewMove方法以前,fromVC爲顯示狀態,而toVC爲未顯示狀態。因此調用時便可實現動畫切換: code

 1 -( void) viewMove:(UIViewController*) fromVC:(UIViewController*)toVC
 2 {
 3     [UIView animateWithDuration: 1 animations:^{
 4         fromVC.view.alpha =  0;
 5         toVC.view.alpha =  1;
 6     } completion:^(BOOL finished) {
 7         fromVC.view.alpha =  0;
 8         toVC.view.alpha =  1;
 9     }];
10 }

1.2 transitionFromViewController 對象

      UIView同時提供了視圖切換方法,同時可支持動畫變換,即transitionFromViewController方法。從而能夠在container viewController中調用該方法來實現視圖的變換。 blog

      如在當前viewController有兩個子視圖控制器:fromVC和toVC,而且fromVC爲顯示狀態。因此能夠調用transitionFromViewController方法來實現動畫切換。

1 -( void) viewMove:(UIViewController*) fromVC:(UIViewController*)toVC
2 {
3 [self transitionFromViewController:fromVC toViewController:toVC duration: 1
4     options:UIViewAnimationOptionTransitionFlipFromLeft |UIViewAnimationOptionShowHideTransitionViews 
5     animations:nil completion:nil
6 ];
7 }

 

2 轉換動畫次序

      轉換動畫是交互兩個view controller的內容,其中存在兩種轉換類型:彈出(presentation)和撤回(dismissal)。

      1) presentation:這種動畫是指在app的view controller層次結構中添加新的view controller;

      2) dismissal:這種動畫是指從app的view controller層次結構中移除view controller。

2.1 轉換delegate

      轉換delegate是一個實現了UIViewControllerTransitioningDelegate協議的對象,其工做是提供以下之一的對象:

       1) Animator對象:

      該對象是實現了UIViewControllerAnimatedTransitioning協議的實體。其功能是負責以動畫的形式顯示或隱藏view controller的視圖。轉換delegate向那些進行presenting 和dismissing操做的實體提供Animator對象。

      2) Interactive animator對象:

       該對象是實現了UIViewControllerInteractiveTransitioning協議的實體。其也是實現動畫操做,但可在動畫過程當中與用戶進行事件交互。

      3) Presentation controller:

      當view controller在屏幕時,presentation controller負責管理彈出(presentation)的樣式。系統已提供了一些內置樣式,同時也支持用戶自定義更多的presentation樣式。

      若要實現自定義動畫,須要設置新彈出view controller的transitioningDelegate屬性爲遵照UIViewControllerTransitioningDelegate協議的對象,同時要修改新彈出view controller的modalPresentationStyle屬性常量UIModalPresentationCustom

圖 312 The custom presentation and animator objects

       如圖 312所示,綠色表示 Presenting view controller(主動彈出),藍色爲Presented view controller(被動彈出),被彈出的VC有個Transitioning Delegate屬性,經過這個屬性可得到Animator、Interactive Animator和Presentation Controller對象。

2.2 執行時序

1) Present view controller

       當被彈出view controller的transitioningDelegate屬性是一個有效對象時,UIKit將按指定的動畫彈出這個view controller。在準備階段UIKit會從UIViewControllerTransitioningDelegate協議(稱爲轉換delegate)調用錯誤! 超連接引用無效。:方法,從而來查詢指定的動畫對象,若該對象有效,則UIKit會按以下步驟執行:

       a) UIKit會調用轉換delegate的interactionControllerForPresentation:方法,若該方法返回nil,那麼UIKit將不與用戶進行交互的執行動畫。

       b) UIKit調用animator對象的transitionDuration:方法來查詢動畫的執行時間。

       c) UIKit調用相應方法開始執行動畫:

               *若爲非交互動畫,則調用animator對象的animateTransition:方法

               *若爲交互動畫,則調用interactive animator的startInteractiveTransition:方法

       d) UIKit等待動畫對象調用context transitioning的completeTransition:方法來等完成動畫。

      在自定義的動畫中,須要手動調用transitionContext對象的completeTransition方法來完成動畫操做。

2) Dismiss view controller

      當消除一個view controller時,UIKit會調用UIViewControllerTransitioningDelegate協議(稱爲轉換delegate)中的animationControllerForDismissedController:方法,並按以下步驟執行程序:

      a) UIKit會調用轉換delegate的interactionControllerForDismissal:方法,若該方法返回nil,那麼UIKit將不與用戶進行交互的執行動畫。

      b) UIKit調用animator對象的transitionDuration:方法來查詢動畫的執行時間。

      c) UIKit調用相應方法開始執行動畫:

                      *若爲非交互動畫,則調用animator對象的animateTransition:方法

                      *若爲交互動畫,則調用interactive animator的startInteractiveTransition:方法

      d) UIKit等待動畫對象調用context transitioning的completeTransition:方法來等完成動畫。

      在自定義的動畫中,須要手動調用transitionContext對象的completeTransition方法來完成動畫操做。

注意:

    當在實現動畫體結束時必須手動調用completeTransition:方法來結束操做。

2.3 轉換Context對象

       在轉換動畫開始以前,UIKit會建立一個transitioning context對象,並將一些如何執行動畫的信息填充到該對象中,這些信息包括Presenting view controller、Presented view controller、container view controller,以及是否與用戶進行交互的信息。其中context對象是實現了UIViewControllerContextTransitioning協議

       如圖 313所示,白色方塊表示container view(不是controller)、綠色表示Presenting view controller、藍色表示Presented view controller,虛線表示引用,實線表示方法。Animator對象經過animateTransition:方法得到context對象,而context經過自身的containerView:方法得到container VC。

圖 313 The transitioning context object

 

2.4 轉換Coordinator對象

        無論是內置的轉換仍是自定義的轉換,UIKit都會建立一個Coordinator對象幫助動畫的執行。除了Present和dismissal view controller外,轉換還可能發生在view cotroller的frame發生變化時。在動畫過程當中,能夠經過Coordinator對象得到一些信息。如圖 314所示,圖形語義與圖 313同樣。

圖 314 The transition coordinator objects

 

3 使用自定義動畫

      使用自定義的動畫實現Present(彈出)view controller,須要進行以下操做:

      1)建立一個Presented view controller;

      2)建立一個已經實現了UIViewControllerTransitioningDelegate協議的對象,並將其賦值給Presented view controller對象的transitioningDelegate屬性

      3)調用presentViewController:animated:completion:方法彈出新的view controller,而且須要將YES傳遞給該方法的animated參數。

4 實現轉換delegate

      如圖 312所示,轉換delegate的功能是爲Present和dismissal操做,提供Animator、Interactive animator

和Presentation controller三種對象。因此實現UIViewControllerTransitioningDelegate協議也就是實現那些可以獲取這三個對象的方法。

以下是實現一個能獲取Animator對象的方法:

1 - (id<UIViewControllerAnimatedTransitioning>)
2             animationControllerForPresentedController:(UIViewController *)presented
3                                 presentingController:(UIViewController *)presenting
4                                   sourceController:(UIViewController *)source 
5     {
6             MyAnimator* animator = [[MyAnimator alloc] init];
7              return animator;
8 }

 

5 實現動畫delegate

      爲了實現UIViewControllerAnimatedTransitioning協議須要實現兩個方法:

      1) transitionDuration: transitionContext:返回動畫持續的時間;

      2) animateTransition: transitionContext:執行具體動畫的方法;

    其中animateTransition方法是重點,主要工做都是在實現該方法中,其中能夠將實現該方法分爲以下三個步驟:

    a) 獲取與動畫有關的參數;

    b) 經過core Animation或UIView animation實現動畫;

    c) 完成動畫操做。

5.1 獲取參數

     當實現animateTransition方法時,其會傳遞一個transitionContext參數,能夠經過這個參數可以得到與動畫相關的信息:

     1) 經過viewControllerForKey:方法得到在轉換時的"from"和"to" view controller;

     2) 經過containerView方法得到動畫的超類,從而將全部的子view添加到這個container view中;

     3) 經過viewForKey:方法得到被添加或被刪除的view,其中在轉換過程當中要麼是被添加,要麼是被刪除的view。

     4) 經過finalFrameForViewController:方法得到最後被添加或刪除後view 的frame矩形。

      因爲動畫對象能夠向Present和dismissal操做提供動畫,因此經過transitionContext獲取的"from"和"to" view controller語義有些差別。如圖 315所示,當執行Present操做時,是將"to" view controller添加到container層次結構中;而當執行dismissal操做時,是將"from" view controller從container層次結構中移除。

圖 315 The from and to objects

5.2 實現動畫

      爲了實現動畫效果,在animateTransition:方法中必須完成兩部分的內容:

      a) 以動畫的形式修改presented view controller中的view位置;

      b) 將presented view controller中的view添加到container view的層次結構中。

1) Presentation 動畫

     對於執行彈出動畫,能夠按以下步驟完成配置:

     a) 經過transitionContext對象的viewControllerForKey: viewForKey:方法得到view controllers 和views對象。

     b) 設置"to" view的起始位置,同時也可修改其它屬性值。

     c) 經過transitionContext對象的finalFrameForViewController:方法獲取"to" view的最終位置。

     d) 將 "to" view添加到container view的層次結構中。

     e) 建立動畫

  • 在animation block中,修改"to" view爲最終的位置。同時也可設置其它屬性的最終值;
  • 在completion block中,調用transitionContext對象的completeTransition::方法完成動畫。

2) Dismissal動畫

      消除view controller的動畫與彈出的操做類似,一樣也可按以下步驟完成配置:

      a)經過transitionContext對象的viewControllerForKey: viewForKey:方法得到view controllers 和views對象。

       b) 計算presented view controller的view(便是"from" view)最終位置,該view爲被清除的view。

      c) 仍是將 "to" view添加爲container view的子view。

      d) 建立動畫

  • 在animation block中,修改"from" view爲最終的位置。同時也可設置其它屬性的最終值;
  • 在completion block中,調用transitionContext對象的completeTransition::方法完成動畫。

5.3 測試實例

1) 場景

      如要實現圖 316所示的兩種動畫操做,在A視圖中彈出B視圖,而且能夠在B視圖將其自身推出返回到A視圖。

圖 316 A custom presentation and dismissal

 

2) 實現動畫Delegate

       用戶須要建立已經實現了UIViewControllerAnimatedTransitioning協議的對象,以下是MyAnimator類的具體實現,在該類的animateTransition:方法中實現了兩種動畫效果(Presentation和Dismissal),其中Presenting爲YES時,表示實現Presentation操做;當Presenting爲NO時,表示實現Dismissal操做,這個Presenting在構造函數中進行初始化。固然也能夠將兩種操做分別實如今不一樣的對象中。

 1 @interface MyAnimator : NSObject <UIViewControllerAnimatedTransitioning>
 2 @property  bool presenting;
 3 -(id) init:(BOOL)presenting;
 4 @end
 5 @implementation MyAnimator
 6 -(id) init:(BOOL)presenting
 7 {
 8      if(self = [super init])
 9     {
10         self.presenting = presenting;
11     }
12      return (self);
13 }
14 -(NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
15 {
16      return  1;
17 }
18 -( void) animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
19 {
20     UIView *containerView = [transitionContext containerView];
21     UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
22     UIViewController *toVC   = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
23     
24     UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
25     UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
26     
27      //  Set up some variables for the animation.
28      CGRect containerFrame = containerView.frame;
29     CGRect toViewStartFrame = [transitionContext initialFrameForViewController:toVC];
30     CGRect toViewFinalFrame = [transitionContext finalFrameForViewController:toVC];
31     CGRect fromViewFinalFrame = [transitionContext finalFrameForViewController:fromVC];
32     
33      //  Set up the animation parameters.
34       if (self.presenting) {
35          //  Modify the frame of the presented view so that it starts
36           //  offscreen at the lower-right corner of the container.
37          toViewStartFrame.origin.x = containerFrame.size.width;
38         toViewStartFrame.origin.y = containerFrame.size.height;
39     }
40      else {
41          //  Modify the frame of the dismissed view so it ends in
42           //  the lower-right corner of the container view.
43          fromViewFinalFrame = CGRectMake(containerFrame.size.width,
44                                         containerFrame.size.height,
45                                         toView.frame.size.width,
46                                         toView.frame.size.height);
47     }
48     
49      //  Always add the "to" view to the container.
50       //  And it doesn't hurt to set its start frame.
51      [containerView addSubview:toView];
52     toView.frame = toViewStartFrame;
53     
54      //  Animate using the animator's own duration value.
55      [UIView animateWithDuration:[self transitionDuration:transitionContext]
56                      animations:^{
57                           if (self.presenting) {
58                               //  Move the presented view into position.
59                               [toView setFrame:toViewFinalFrame];
60                          }
61                           else {
62                               //  Move the dismissed view offscreen.
63                               [fromView setFrame:fromViewFinalFrame];
64                          }
65                      }
66                      completion:^(BOOL finished){
67                          BOOL success = ![transitionContext transitionWasCancelled];
68                          
69                           //  After a failed presentation or successful dismissal, remove the view.
70                            if ((self.presenting && !success) || (!self.presenting && success)) {
71                              [toView removeFromSuperview];
72                          }
73                          
74                           //  Notify UIKit that the transition has finished
75                           [transitionContext completeTransition:success];
76                      }];
77 }

3) 實現轉換Delegate

      在彈出動畫前,須要先給Presented view controller對象設置transitioningDelegate屬性,而設置的對象爲UIViewControllerTransitioningDelegate協議的實現類。以下是一種實現方式,因爲被彈出的view controller有兩種動畫效果(Presentation和Dismissal),因此須要實現animationControllerForPresentedControlleranimationControllerForDismissedController方法分別彈出兩種不一樣的動畫對象。

 1 @interface NSObject: UIViewController <UIViewControllerTransitioningDelegate>
 2 @end
 3 @implementation myTransitioningDelegate
 4 - (id<UIViewControllerAnimatedTransitioning>)
 5 animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
 6 {
 7     MyAnimator *animator = [[MyAnimator alloc] init:YES];
 8     
 9      return animator;
10 }
11 -(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
12 {
13     MyAnimator *animator = [[MyAnimator alloc] init:NO];
14      return animator;
15 }
16 @end

 

4) 實現彈出操做

       上述的轉換delegate和動畫delegate都已經實現完成,接下來就是要實現轉換視圖的響應方法。即firstViewController爲圖 316的A視圖控制器,而thirdViewController爲圖 316的B視圖控制器。而customAnimation:方法爲按鈕的響應方法,在該方法中調用presentViewController方法來彈出thirdViewController視圖(即B視圖)。

       其中須要注意的是mtd屬性不能將其聲明爲customAnimation方法內的局部變量,不然將致使Dismissal操做不能實現動畫,由於在該方法推出後,mtd變量的生命週期將結束,從而在Dismissal操做時其已經無效,因此這裏將mtd聲明爲成員屬性,固然若firstViewController類自身實現了UIViewControllerAnimatedTransitioning協議,那麼能夠將transitioningDelegate設置爲self。

 1 @interface firstViewController : UIViewController <UIViewControllerTransitioningDelegate>
 2 {
 3     myTransitioningDelegate *mtd;
 4 }
 5 @end
 6 - (IBAction)customAnimation:(id)sender {
 7     thirdViewController *thirdVC = [self.storyboard instantiateViewControllerWithIdentifier: @" thirdViewController "];
 8     
 9     mtd = [[myTransitioningDelegate alloc] init];
10     thirdVC.transitioningDelegate = mtd;
11     thirdVC.modalPresentationStyle = UIModalPresentationCustom;
12     
13     [self presentViewController:thirdVC animated:YES completion:nil];
14 }

5) 實現退出操做

      在被彈出視圖(B視圖)中,只需實現退出按鈕的響應方法(dismissal),從而在該方法中調用dismissViewControllerAnimated方法推出操做。

1 @implementation thirdViewController
2 - (IBAction)dismissal:(id)sender
3 {
4     [self dismissViewControllerAnimated:YES completion:nil];
5 }
6 @end

6 添加交互對象

      UIKit支持添加一個交互對象,經過這個對象用戶能在進行彈出等操做時,控制動畫的動做。爲轉換delegate添加一個交互對象也很是簡單,只需在其interactionControllerForPresentation方法中返回一個遵照UIViewControllerInteractiveTransitioning協議的對象便可。能夠直接實現該協議,也能夠繼承UIPercentDrivenInteractiveTransition類,但都需重載startInteractiveTransition方法。

     最後須要注意的是,因爲轉換動畫仍是執行UIViewControllerTransitioningDelegate協議中animationControllerForPresentedController方法返回的對象,但動畫開始執行的時間還是在調用finishInteractiveTransition方法後。

以下所示的實例:

1) 實例場景

      用戶在點擊視圖的按鈕後,須要再點擊視圖中的空白區域,才能實現轉換及動畫。從而本例只是在3.3.5.3小節所示的基礎上添加一個交互對象,因此對其它內容都無需修改,只是添加的交互類myInteractiveAnimator,及在myTransitioningDelegate類中添加了一個interactionControllerForPresentation方法,在該方法中返回一個交互對象。

2) 實現交互類

     本例採用繼承UIPercentDrivenInteractiveTransition類的方式實現交互,從而實現了startInteractiveTransition方法。在該方法中添加了一個點擊手勢識別器,並在響應方法中啓動動畫轉換。

 1 @interface myInteractiveAnimator : UIPercentDrivenInteractiveTransition
 2 @property UITapGestureRecognizer* panGesture;
 3 @end
 4 @implementation myInteractiveAnimator
 5 - ( void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext
 6  {
 7      //  Always call super first.
 8  [super startInteractiveTransition:transitionContext];
 9  //  Add the gesture recognizer to the container view.
10      self.panGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleSwipeUpdate:)];
11     UIView* container = [transitionContext containerView];
12     [container addGestureRecognizer:self.panGesture];
13 }
14 -( void)handleSwipeUpdate:(UIGestureRecognizer *)gestureRecognizer 
15 {
16     NSLog( @" hello world ");
17     [self finishInteractiveTransition];
18 }
19 @end

3) 返回交互對象

     以下是在3.3.5.3小節所示的myTransitioningDelegate類基礎上添加了interactionControllerForPresentation方法,其它方法都是原來的內容。

 1 @implementation myTransitioningDelegate
 2 - (id<UIViewControllerAnimatedTransitioning>)
 3 animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
 4 {
 5     MyAnimator *animator = [[MyAnimator alloc] init:YES];
 6      return animator;
 7 }
 8 -(id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
 9 {
10     MyAnimator *animator = [[MyAnimator alloc] init:NO];
11      return animator;
12 }
13 - (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator
14 {
15     myInteractiveAnimator *myAnimator = [[myInteractiveAnimator alloc] init];
16      return myAnimator;
17 }
18 @end

 

iOS UIKit:viewController文章參考文獻

      [1] View Controller Programming Guide for IOS

      [2] 讓iPhone也能popup的效果  

      [3] 建立Unwind Segue

      [4] UIViewControllerTransitioningDelegate Protocol Reference.

      [5] UIViewControllerContextTransitioning Protocol Reference.

      [6] UIViewControllerInteractiveTransitioning Protocol Referrence

相關文章
相關標籤/搜索