iOS CAAnimation之CATransition (自定義轉場動畫工具類)

weixin_34138377發表於2017-01-26
4185621-8bb7c509815fac5a.gif
transitionDemo

1.github原始碼:簡書作者wazrx
2.OC:轉場動畫的管理工具類(無互動)
3.swift: 轉場動畫的管理工具類(無互動)

oc 鏈式配置
轉場動畫樣式 以及時間 配置

self.presentConfig
    .setUpPresentStyle(presentStyle)
    .setUpDismissStyle(dismissStyle)
    .setUpPresentDuration(0.4)
    .setUpDismissDuration(0.4)
    .setUpIsLinkage(true);

modal 的view 的陰影設定

self.shadowAnimationConfig
    .setUpDismissShadowColor([UIColor blueColor])
    .setUpPresentShadowColor([UIColor blueColor])
    .setUpPresentShadowOpacity(0.4)
    .setUpDismissShadowOpacity(0.2)
    .setUpDismissShadowOffset(CGSizeMake(1, 1))
    .setUpPresentShadowOffset(CGSizeMake(-20, 10))
    .setUpDismissShadowRadius(20)
    .setUpPresentShadowRadius(30);

一、 參考資料:

1.王巍 《部落格》(非常詳細,推薦閱讀)
https://onevcat.com/2013/10/vc-transition-in-ios7/
2.xiao333ma 《部落格》
http://blog.csdn.net/xiao333ma/article/details/49028241#t1
3.VincentHK iOS 檢視控制器轉場詳解

工具pod 匯入

pod 'PYTransitionAnimater'

二、protocol

1.@protocol UIViewControllerContextTransitioning

  1. 這個介面用來提供切換上下文給開發者使用,包含了從哪個VC到哪個VC等各類資訊,一般不需要開發者自己實現。具體來說,iOS7的自定義切換目的之一就是切換相關程式碼解耦,在進行VC切換時,做切換效果實現的時候必須要需要切換前後VC的一些資訊。
  2. 重要的方法:
-(UIView *)containerView; 
VC切換所發生的view容器,開發者應該將切出的view移除,將切入的view加入到該view容器中。
-(UIViewController *)viewControllerForKey:(NSString *)key; 
提供一個key,返回對應的VC。現在的SDK中key的選擇只有:
UITransitionContextFromViewControllerKey表示將要切出VC。
UITransitionContextToViewControllerKey表示將要切入的VC。
-(CGRect)initialFrameForViewController:(UIViewController *)vc; 
某個VC的初始位置,可以用來做動畫的計算。
-(CGRect)finalFrameForViewController:(UIViewController *)vc; 
與上面的方法對應,得到切換結束時某個VC應在的frame。
-(void)completeTransition:(BOOL)didComplete; 
向這個context報告切換已經完成。

2.@protocol UIViewControllerAnimatedTransitioning

  1. 這個介面負責切換的具體內容,也即“切換中應該發生什麼”。開發者在做自定義切換效果時大部分程式碼會是用來實現這個介面。
  1. 重要的方法:
-(NSTimeInterval)transitionDuration:(id < UIViewControllerContextTransitioning >)transitionContext; 
系統給出一個切換上下文,我們根據上下文環境返回這個切換所需要的花費時間
(一般就返回動畫的時間就好了,SDK會用這個時間來在百分比驅動的切換中進行幀的計算)
-(void)animateTransition:(id < UIViewControllerContextTransitioning >)transitionContext;
 在進行切換的時候將呼叫該方法,我們對於切換時的UIView的設定和動畫都在這個方法中完成。

3.@protocol UIViewControllerTransitioningDelegate

  1. 這個介面的作用比較簡單單一,在需要VC切換的時候系統會像實現了這個介面的物件詢問是否需要使用自定義的切換效果。
  2. 這個介面共有四個類似的方法:
    前兩個方法是針對動畫切換的,我們需要分別在呈現VC和解散VC時,給出一個實現了UIViewControllerAnimatedTransitioning介面的物件(其中包含切換時長和如何切換)。後兩個方法涉及互動式切換
-(id< UIViewControllerAnimatedTransitioning >)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source;
-(id< UIViewControllerAnimatedTransitioning >)animationControllerForDismissedController:(UIViewController *)dismissed;
-(id< UIViewControllerInteractiveTransitioning >)interactionControllerForPresentation:(id < UIViewControllerAnimatedTransitioning >)animator;
-(id< UIViewControllerInteractiveTransitioning >)interactionControllerForDismissal:(id < UIViewControllerAnimatedTransitioning >)animator;

三、沒有互動的程式碼具體思想步驟

情景: VC_a跳到VC_b ,注意,轉場動畫不難,但是套路一定要明顯,我們都是有原則的人。戳這裡看原始碼

1. 建立VC_aVC_a可以什麼都不用做,直接presen

2. 構建Animatr轉場動畫的工具類(繼承NSObject遵守<UIViewControllerTransitioningDelegate>協議)

  1. Animatr中實現代理方法
//present
-(id< UIViewControllerAnimatedTransitioning >)animationControllerForDismissedController:(UIViewController *)dismissed;
//dismiss
-(id<UIViewControllerAnimatedTransitioning>) animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
  1. 建立AnimatedTransition(繼承NSObject遵守<UIViewControllerAnimatedTransitioning>協議,在Animatr的方法中建立並返回)
  1. 你會發現,返回值需要遵守UIViewControllerAnimatedTransitioning協議的id型別的類
    於是 建立AnimatedTransition
  2. 內部實現兩個方法
    . 返回動畫時長
//1. 返回一個動畫時長,這個時長儘量要與實際動畫時長一直,因為系統會以此來作為轉場參考時間
-(NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
//2. 具體動畫的實現類
-(void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext{
    self.transitionContext = transitionContext;
    //1. 獲取到當前VC 目標VC
    UIViewController * fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    UIViewController * toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    //2. 獲取當前的容器檢視
    UIView * contentView = [transitionContext containerView];
    //3. 新增fromView,與toView(不能直接fromeVC.view獲取)
    UIView *toView = [transitionContext viewForKey:(UITransitionContextToViewKey)];
    UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
    //[contentView addSubview:fromView];
    [contentView addSubview:toView];
    //4. contentView 設定蒙版 (這裡是灰色的蒙版)
    contentView.backgroundColor = [UIColor colorWithWhite:0.8 alpha:0.5];
    //5. 動畫實現 分為present動畫和dismiss動畫 (可以在Animatr的代理方法中區分並傳進來)
    if(present){
        [UIView animateWithDuration:0.3 animations:^{
//這裡不能用toView.view獲取toView
        }completion:^(BOOL finished) {
            //注意:完成動畫要呼叫[transitionContext completeTransition:YES];告訴系統完成動畫了,否則就會造成toView不能相應點選的情況,可能在執行完動畫後,系統回到了主執行緒,一直在等待完成命令,所以沒有辦法執行點選事件
            [transitionContext completeTransition:YES];
        }];
    }else if(dismiss){
        [UIView animateWithDuration:0.3 animations:^{
        //這時候,必須要用toVC.View獲取toView
        } completion:^(BOOL finished) {
            //注意:同present如果不告訴系統完成動畫了就會直接dismiss掉fromeVC不會有任何動畫,(這裡的dismiss只是把present時候的toVC與fromeVC顛倒了)
            [transitionContext completeTransition:YES];
        }];
  }    
}
  1. VC_b: 在init方法裡面設定delegatemodalPresentationStyle
-(instancetype)init
{
    self = [super init];
    if (self) {
 //這個屬性表示在modal、dismiss的時候會走自定義的方法
        self.transitioningDelegate = self;
// 這個方法保證fromView才不會被移除(及可以在modal後看到a控制器的view)
 self.modalPresentationStyle = UIModalPresentationCustom;
        self.modalPresentationStyle = UIModalPresentationCustom;
    }
    return self;
}

四、有互動程式碼實現思路 —— 手勢驅動的百分比切換

1. 建立一個UIPercentDrivenInteractiveTransition 類

1.這是一個實現了UIViewControllerInteractiveTransitioning介面的類,為我們預先實現和提供了一系列便利的方法,可以用一個百分比來控制互動式切換的過程。一般來說我們更多地會使用某些手勢來完成互動式的轉移。

2. 本類中重要的方法

-(void)updateInteractiveTransition:(CGFloat)percentComplete;
//更新百分比,一般通過手勢識別的長度之類的來計算一個值,然後進行更新。

-(void)cancelInteractiveTransition ;
//報告互動取消,返回切換前的狀態

–(void)finishInteractiveTransition;
 //報告互動完成,更新到切換後的狀態

3. 給view新增手勢(對於手勢的總結請看 iOS 手勢的基本介紹


五、對於坑

1. dismiss轉場結束後出現黑屏:
參考:iOS 檢視控制器轉場詳解

Custom 模式:presentation 結束後,presentingView(fromView) 未被主動移出檢視結構,在 dismissal 中,注意不要像其他轉場中那樣將 presentingView(toView) 加入 containerView 中,否則 dismissal 結束後本來可見的 presentingView 將會隨著 containerView 一起被移除。如果你在 Custom 模式下沒有注意到這點,很容易出現黑屏之類的現象而不知道問題所在。
在 Custom 模式下的dismissal 轉場(在present中要新增)中不要像其他的轉場那樣將 toView(presentingView) 加入 containerView,否則 presentingView 將消失不見,而應用則也很可能假死。而 FullScreen 模式下可以使用與前面的容器類 VC 轉場同樣的程式碼。因此,上一節裡示範的 Slide 動畫控制器不適合在 Custom 模式下使用,放心好了,Demo 裡適配好了,具體的處理措施,請看下一節的處理

六、程式碼

1.github原始碼:簡書作者wazrx
2.OC:轉場動畫的管理工具類(無互動)
3.swift: 轉場動畫的管理工具類(無互動)

相關文章