iOS 轉場動畫探究(二)

這篇文章是接着第一篇寫的,要是有同行剛看到的話建議從前面第一篇看,這是第一篇的地址:iOS 轉場動畫探究(一)ios

接着上一篇寫的內容:git

       上一篇iOS 轉場動畫探究(一)咱們說到了轉場要素的第四點,把那個小實例解釋完,這篇還有一點咱們接着總結:github

       Demo的下載地址這裏再發一次: 這裏是Demo的下載地址app

 

五、  轉場協調器協議 UIViewControllerTransitionCoordinator框架

       能夠經過須要產生動畫效果的視圖控制器的transitionCoordinator屬性來獲取轉場協調器,轉場協調器只在轉場動畫的執行過程當中存在。也正是由於有了UIViewControllerTransitionCoordinator ,咱們纔可在轉場動畫發生的同時並行執行其餘的動畫。好比像咱們第三個小例子裏面後面半透明背景動畫,就是經過這個UIViewControllerTransitionCoordinator咱們來作的,主要在 Modal 轉場和交互轉場取消時使用,其餘時候不多用到,咱們看看它裏面的幾個方法:ide

// Any animations specified will be run in the same animation context as the
// transition. If the animations are occurring in a view that is a not
// descendent of the containerView, then an ancestor view in which all of the
// animations are occuring should be specified.  The completionBlock is invoked
// after the transition completes. (Note that this may not be after all the
// animations specified by to call complete if the duration is not inherited.)
// It is perfectly legitimate to only specify a completion block. This method
// returns YES if the animations are successfully queued to run. The completions
// may be run even if the animations are not. Note that for transitioning
// animators that are not implemented with UIView animations, the alongside
// animations will be run just after their animateTransition: method returns.
//
- (BOOL)animateAlongsideTransition:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))animation
                        completion:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))completion;

// This alternative API is needed if the view is not a descendent of the container view AND you require this animation
// to be driven by a UIPercentDrivenInteractiveTransition interaction controller.
- (BOOL)animateAlongsideTransitionInView:(nullable UIView *)view
                               animation:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))animation
                              completion:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))completion;

// When a transition changes from interactive to non-interactive then handler is
// invoked. The handler will typically then do something depending on whether or
// not the transition isCancelled. Note that only interactive transitions can
// be cancelled and all interactive transitions complete as non-interactive
// ones. In general, when a transition is cancelled the view controller that was
// appearing will receive a viewWillDisappear: call, and the view controller
// that was disappearing will receive a viewWillAppear: call.  This handler is
// invoked BEFORE the "will" method calls are made.
- (void)notifyWhenInteractionEndsUsingBlock: (void (^)(id <UIViewControllerTransitionCoordinatorContext>context))handler NS_DEPRECATED_IOS(7_0, 10_0,"Use notifyWhenInteractionChangesUsingBlock");

// This method behavior is identical to the method above. On 10.0, however, the behavior has
// changed slightly to account for the fact that transitions can be interruptible. For interruptible transitions
// The block may be called multiple times. It is called each time the transition moves from an interactive to a 
// non-interactive state and vice-versa. The block is now also retained until the transition has completed.
- (void)notifyWhenInteractionChangesUsingBlock: (void (^)(id <UIViewControllerTransitionCoordinatorContext>context))handler NS_AVAILABLE_IOS(10_0);

 

       結合上面的英文註釋看看這幾個方法,在博客上看到關於這個協議的翻譯,確定翻譯,再吧一些地方本身總結了一下,直接寫出來,對照着上面的理解一下這個協議:函數

       一、 你可使用一個轉場協調器對象執行一個與轉場相關的任務,它將分離動畫控制器正在作的事。在轉場期間,動畫控制器對象負責把視圖控制器的內容呈如今屏幕上,可是可能也有一些其餘的可視元素一樣須要被展現。好比,一個顯示控制器可能想執行顯示或者使一些裝飾視圖消失從視圖控制器內容裏分離出的動畫。這種狀況下,可使用轉場協調器來執行這些動畫。學習

       二、轉場協調器和動畫控制器對象一塊工做,確保任何額外動畫被執行在一樣的動畫組中,就像轉場動畫同樣。在同樣的組擁有動畫,意味着它們在一樣的時間執行,而且能夠響應一個動畫控制器對象提出的任什麼時候間改變。這些時間調整會自動發生,不須要寫額外的代碼在你的項目中。動畫

       三、使用轉場協調器處理視圖層次動畫比在viewWillappear:方法中作出一樣的改變,或者相同的方法在你的視圖控制器中要好不少。你用這個協議中的方法所註冊的block會確保執行同樣的轉場動畫。更重要的是,轉場協調器會提供重要的信息關於轉場的狀態,好比是否它會被取消,對於你的動畫block而言,經過ui

UIViewControllerTransitionCoordinatorContext對象。

       四、除了在轉場期間執行註冊動畫,你能夠調用notifyWhenInteractionChangesUsingBlock: 方法註冊一個block來清理和用戶交互的轉場動畫。清理很是重要,當用戶取消轉場交互時,當取消的時候,你須要返回一個原始的視圖層次狀態,就像以前轉場存在的同樣。

 

咱們在協議的最上面會看到這樣一句話:

 

        翻譯說明:一個採用UIViewControllerTransitionCoordinator協議的對象能夠給控制器轉場動畫提供相關支持。通常狀況下,你不須要採用這個協議在本身定義的類中。當presentation/dismissal一個視圖控制器時,UIKit會自動建立一個轉場協調器對象,而且給視圖控制器的transitionCoordinator屬性賦值(這一點在接下來的實例中,你會看的到的),這個轉場協調器對象是短暫的,而且延續到轉場動畫的結束。

       說第三個小例子以前咱們還得熟悉一下這個:UIPresentationController,它提供了四個函數來定義present和dismiss動畫開始先後的操做:

       1、presentationTransitionWillBegin: present將要執行時

       二、presentationTransitionDidEnd:    present執行結束後

       三、dismissalTransitionWillBegin:    dismiss將要執行時

       四、dismissalTransitionDidEnd:         dismiss執行結束後

      這四個方法在咱們的實例中有用到,咱們在下面代碼裏面還會說。

 

EXAMPLE-THREE:

 

看上面效果圖的第三個實例:

      在第三個Demo中,也就是底部卡片的呈現形式中,咱們把UIViewControllerTransitioningDelegate和UIViewControllerAnimatedTransitioning都寫在了CustomPresentationController當中,這個CustomPresentationController就是集成與咱們前面提到過的UIPresentationController,這個UIPresentationController前面提到的時候說的什麼能夠回憶一下,再在代碼中去理解:

      從初始化方法開始瞭解,說說咱們須要注意的地方:

一、初始化

/**
 初始化

 @param  presentedViewController  presentedViewController  跳轉到這個控制器
 @param  presentingViewController presentingViewController 由這個控制器開始跳轉
 @return return value description
 */
- (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(nullable UIViewController *)presentingViewController{
 
        self =[super initWithPresentedViewController:presentedViewController presentingViewController:presentingViewController];
        if (self) {
                
                // 自定義modalPresentationStyle
                presentedViewController.modalPresentationStyle= UIModalPresentationCustom;
                
        }
        return self;
}

這裏主要的只有一點:presentedViewController.modalPresentationStyle= UIModalPresentationCustom

 

二、presentationTransitionWillBegin 這個方法

     在這個方法裏面背景圖的動畫就是在咱們第五點強調的UIViewControllerTransitionCoordinator當中

/* present將要執行時 */
- (void)presentationTransitionWillBegin
{
       
        // 設置presentationWrappingView和dimmingView的UI效果
        UIView * presentedViewControllerView = [super presentedView];
        presentedViewControllerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
        
        {
                // 
                UIView *presentationWrapperView = [[UIView alloc] initWithFrame:self.frameOfPresentedViewInContainerView];
                presentationWrapperView.layer.shadowOpacity = 0.44f;  //設置陰影的透明度(0~1之間,0表示徹底透明)
                presentationWrapperView.layer.shadowRadius  = 13.f;   //設置陰影的圓角
                
                //設置陰影的偏移量,若是爲正數,則表明爲往右邊偏移
                presentationWrapperView.layer.shadowOffset  = CGSizeMake(0, -6.f);
                self.presentationWrappingView = presentationWrapperView;
                
                // 圓角View
                UIView *presentationRoundedCornerView = [[UIView alloc] initWithFrame:UIEdgeInsetsInsetRect(presentationWrapperView.bounds, UIEdgeInsetsMake(0, 0, -CORNER_RADIUS, 0))];
                
                // autoresizingMask 這個屬性 自動調整與父視圖之間的邊界距離
                presentationRoundedCornerView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
                presentationRoundedCornerView.layer.cornerRadius = CORNER_RADIUS;
                presentationRoundedCornerView.layer.masksToBounds = YES;
                [presentationRoundedCornerView addSubview:presentedViewControllerView];
                
                //*** presentedViewControllerView
                presentedViewControllerView.frame = UIEdgeInsetsInsetRect(presentationRoundedCornerView.bounds, UIEdgeInsetsMake(0, 0, CORNER_RADIUS, 0));

                // ******************
                [presentationWrapperView addSubview:presentationRoundedCornerView];

        }
        
        {
                // 背景圖
                UIView *dimmingView = [[UIView alloc] initWithFrame:self.containerView.bounds];
                dimmingView.backgroundColor = [UIColor blackColor];
                dimmingView.opaque = NO;
                dimmingView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
                [dimmingView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dimmingViewTapped:)]];
                self.dimmingView = dimmingView;
                [self.containerView addSubview:dimmingView];
                
                id<UIViewControllerTransitionCoordinator> transitionCoordinator = self.presentingViewController.transitionCoordinator;
                
                self.dimmingView.alpha = 0.f;
                [transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
                        
                        self.dimmingView.alpha = 0.5f;
                        
                } completion:NULL];
        }
}

剩下的UIViewControllerAnimatedTransitioning和UIViewControllerTransitioningDelegate裏面的東西咱們就不在重複去說。

 

咱們多看幾個例子:

EXAMPLE-FOUR

      這個彈性POP的例子主要是根據 CGAffineTransform 這個屬性來展開的,你要了解這個就比較好寫了,主要的全都在代碼註釋裏面,效果圖就在下面:

      這個例子我就強調下面這一個地方,下面這段代碼:

 BOOL isPresent   = (toViewController.presentingViewController == fromViewController);

        UIView *tempView = nil;
        if (isPresent) {
                
           tempView = [fromViewController.view snapshotViewAfterScreenUpdates:NO];
                
           tempView.frame = fromViewController.view.frame;
           fromViewController.view.hidden = YES;
           [contextView addSubview:tempView];
           [contextView addSubview:toViewController.view];
           toViewController.view.frame = CGRectMake(0, contextView.frame.size.height, contextView.frame.size.width, 400);
                
        }else{
        
                //參照present動畫的邏輯,present成功後,containerView的最後一個子視圖就是截圖視圖,咱們將其取出準備動畫
                NSArray *subviewsArray = contextView.subviews;
                tempView = subviewsArray[MIN(subviewsArray.count, MAX(0, subviewsArray.count - 2))];
        }

      注意一下這個方法: snapshotViewAfterScreenUpdates: 剩下的看代碼就OK了,這幾個例子我感受其實帶的框架都是同樣的,不同的就是裏面的動畫具體的實現,先看看咱們四、五、6說的這幾個Demo的效果:

 

 

EXAMPLE-FIVE

      圓點擴散這個Demo主要的就是靈活的使用了UIBezierPath 和 CABasicAnimation,其實還要掌握了轉場的本質,感受剩下的真的就看你能想到哪裏了!哈哈.....

      這個Demo全部的核心都在下面兩個方法裏面:

      -(void)presentViewController:(id <UIViewControllerContextTransitioning>)transitionContext

      -(void)dismissViewController:(id <UIViewControllerContextTransitioning>)transitionContext

      具體的代碼我就再也不說了,代碼上面的註釋是足夠了的,很詳細.....仍是看代碼吧。

 

EXAMPLE-SIX

      導航控制器的轉場

      最後的這個翻頁效果的Demo,其實你看着它像3D的感受,你想起了 CATransform3D 感受就夠了,經過下面這個方法來開始咱們的導航轉場:

-(void)presentNextControllerClicked{
    
    // 既然這裏是導航控制器的轉場,就要給導航設置代理。而後在第二個控制器遵照相應的協議以後,判斷
    // 是Push仍是Pop,執行相應的動畫
    PageToViewController * pushVC = [PageToViewController new];
    self.navigationController.delegate = pushVC;
    [self.navigationController pushViewController:pushVC animated:YES];
}

       下面要注意的就是導航操做的判斷,是Push仍是Pop,咱們就在導航代理的下面方法裏面判斷,重點應用的就是代理方法裏面的operation參數:

#pragma mark - UINavigationControllerDelegate
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC{

    //分pop和push兩種狀況分別返回動畫過渡代理相應不一樣的動畫操做
    return [[PageAnimation alloc]initWith:operation == UINavigationControllerOperationPush ? Push_type : Pop_type];

}

        

      注意到上面說的兩點,剩下的又回到咱們最開始的--動畫了!動畫部分的代碼就不粘貼出來佔用篇幅了,仍是那句下載下來本身去看看! 

      你看着上面給的效果圖,要有興趣就去下載代碼看看,源碼當中仍是有不少的細節的,我也加了註釋,但願上面全部的東西以及源碼裏面的內容能幫助到你們!

最後:

      這個上面的暫時就告一段落了,後面有新的動向我會在接着更新,下面是我學習的過程當中,看過的相關的博客!感謝做者......

      Demo的下載地址這裏再發一次: 這裏是Demo的下載地址

      iOS自定義轉場動畫實戰講解

      iOS自定義轉場動畫

      iOS視圖控制器轉場詳解

      WWDC 2013 Session筆記 - iOS7中的ViewController切換

相關文章
相關標籤/搜索