一、介紹
在iOS開發中,轉場動畫的使用無處不見,不隻是我們自己更多的使用UIViewblock動畫實作一個轉場動畫,其實,在我們實作VC控制器跳轉的時候都是轉場動畫的實作,例如标簽欄控制器的切換、模态動畫present和dismiss、導航控制器的push和pop。實作它們的轉場動畫,隻需要實作它們的動畫協定即可,說起來有點太籠統,不如看下面的圖吧:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5SZmhTO3kDZhNzNhJzNjJTOyYTO3QGZwMzM1QDOycDN48CX0IzLcRDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjLzM3Lc9CX6MHc0RHaiojIsJye.png)
二、分析
對于上面的三種類型的控制器,系統都會為它們設定一個代理,通過這個代理方法去監測它們切換VC的過程,這個過程僅僅是出現和消失的過程,至于這個過程是什麼過渡效果,這個代理是不管的。要想這個過程是有動畫的,那麼在這些過程中,也就是代理函數中,需要另外再傳回一個實作動畫的對象,這個對象必須遵循實作動畫的協定,在這個協定中開發者可以重寫自定義轉場動畫。下面會慢慢示範這三種類型控制器的自定義轉場動畫。
重寫不可互動轉場動畫的核心協定内容:
//重寫動畫協定@protocol UIViewControllerAnimatedTransitioning//動畫執行時間- (NSTimeInterval)transitionDuration:(nullable id )transitionContext;//自定義動畫效果- (void)animateTransition:(id )transitionContext;@end
重寫可互動轉場動畫的核心協定内容:
//重寫動畫協定@protocol UIViewControllerInteractiveTransitioning//自定義動畫效果- (void)startInteractiveTransition:(id )transitionContext;@end
系統提供的一個百分比可互動轉場動畫核心類内容:
//系統提供的百分比動畫類,已經遵循了可互動協定@interface UIPercentDrivenInteractiveTransition : NSObject
三、轉場動畫View之間的切換
四、實作一個自定義的模态動畫
1、概述
正如我們所知,系統為我們提供的模态動畫預設是從底部present出,然後dismiss回到底部。 雖然說這個基本能夠滿足使用,但是如果我們還想使用其他形式的模态動畫例如從頂部present出dismiss回到頂部,這個時候就需要對系統預設的轉場動畫進行自定義了。
2、詳解
(1)要自定義模态轉場動畫,首先需要給被模态的控制器設定一個實作了UIViewControllerAnimatedTransitioning協定的代理,這些協定方法可以監測動畫執行的過程,代理和協定如下:
//代理@protocol UIViewControllerTransitioningDelegate;@interface UIViewController(UIViewControllerTransitioning)
@property (nullable, nonatomic, weak) id transitioningDelegate API_AVAILABLE(ios(7.0));@end
//協定
@protocol UIViewControllerTransitioningDelegate
(2)然後在上面的協定方法中傳回一個實作了UIViewControllerAnimatedTransitioning協定的代理,在這個代理的協定方法中可以真正重寫轉場動畫了,協定如下:
@protocol UIViewControllerAnimatedTransitioning//動畫執行時間- (NSTimeInterval)transitionDuration:(nullable id )transitionContext;//自定義轉場動畫- (void)animateTransition:(id )transitionContext;
@optional
(3)自定義轉場動畫實作如下【注意:Singleton單例類和UIView+Extesion分類需要自己去拷貝引入】
- 設定UIViewControllerAnimatedTransitioning代理對象TransitionDelegate,監測動畫執行過程,将其設定為單例
#import #import "Singleton.h"@interface TransitionDelegate : NSObjectSingletonH(TransitionDelegate);@end
#import "TransitionDelegate.h"#import "CustomAnimationTransition.h"@implementation TransitionDelegate SingletonM(TransitionDelegate);#pragma mark -//展示的動畫- (id )animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init]; animation.presented = YES; return animation; }//關閉的動畫- (id )animationControllerForDismissedController:(UIViewController *)dismissed { CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init]; animation.presented = NO; return animation; }@end
- 設定UIViewControllerAnimatedTransitioning代理對象CustomTransitionAnimationTransition,重寫動畫效果
#import @interface CustomAnimationTransition : NSObject//判斷是present還是dismiss, YES:present NO:dismisss@property (assign,nonatomic)BOOL presented;@end
-
//設定過渡動畫(modal和dismiss的動畫都需要在這裡處理)- (void)animateTransition:(id )transitionContext { // UITransitionContextToViewKey, // UITransitionContextFromViewKey. //出來的動畫 if (self.presented) { //擷取并添加轉場視圖 UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey]; [transitionContext.containerView addSubview:toView]; //設定動畫從上往下出來 toView.y = -toView.height; [UIView animateWithDuration:duration animations:^{ toView.y = 0; } completion:^(BOOL finished) { //移除視圖 BOOL cancle = [transitionContext transitionWasCancelled]; if (cancle) { [toView removeFromSuperview]; } //動畫完成後,視圖上的事件才能處理 [transitionContext completeTransition:!cancle]; }]; } //銷毀的動畫 else { //擷取轉場視圖 UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey]; [UIView animateWithDuration:duration animations:^{ fromView.y = -fromView.height; } completion:^(BOOL finished) { //移除視圖 BOOL cancle = [transitionContext transitionWasCancelled]; if (!cancle) { [fromView removeFromSuperview]; } //動畫完成後,視圖上的事件才能處理 [transitionContext completeTransition:!cancle]; }]; } }
- 開始執行,結果如gif圖
//presentUINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc] init]]; nav.transitioningDelegate = [TransitionDelegate sharedTransitionDelegate];//自定義轉場動畫nav.modalPresentationStyle = UIModalPresentationFullScreen; [self presentViewController:nav animated:YES completion:nil];
-
iOS:探究視圖控制器的轉場動畫
(4)我們已經實作了一個簡單的自定義模态不可互動的轉場動畫,其實,在模态控制器的時候,我們還可以自定義可互動的轉場動畫以及設定自定義的模态風格。可互動的轉場動畫一會兒再讨論,先來讨論一下模态風格,系統在iOS13之前預設都是滿屏模式的UIModalPresentationFullScreen,但是iOS13之後,預設是UIModalPresentationPageSheet。系統提供的模态風格如下:
//模态風格枚舉typedef NS_ENUM(NSInteger, UIModalPresentationStyle) {
UIModalPresentationFullScreen = 0,
UIModalPresentationPageSheet ,
UIModalPresentationFormSheet ,
UIModalPresentationCurrentContext ,
UIModalPresentationCustom , //自定義
UIModalPresentationOverFullScreen ,
UIModalPresentationOverCurrentContext ),
UIModalPresentationPopover ,
UIModalPresentationBlurOverFullScreen ,
UIModalPresentationNone,
UIModalPresentationAutomatic ,
};
(5)從上面的枚舉可以看到,系統是支援我們實作自己的風格的,也就是自定義。在實作自定義之前,一定得知道UIPresentationController這個類,這個是彈出框控件,模态的控制器都是由它進行管理,主要代碼如下:
//重寫此方法可以在彈框即将顯示時執行所需要的操作- (void)presentationTransitionWillBegin;
//重寫此方法可以在彈框顯示完畢時執行所需要的操作- (void)presentationTransitionDidEnd:(BOOL)completed;
//重寫此方法可以在彈框即将消失時執行所需要的操作- (void)dismissalTransitionWillBegin;
//重寫此方法可以在彈框消失之後執行所需要的操作- (void)dismissalTransitionDidEnd:(BOOL)completed;
//重寫決定了彈出框的frame- (CGRect)frameOfPresentedViewInContainerView;
//重寫對containerView進行布局- (void)containerViewWillLayoutSubviews;- (void)containerViewDidLayoutSubviews;
//初始化方法- (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController;
(6)額外再提一個知識點,因為一會兒在自定義模态風格時會涉及到。在本文開篇結構圖中介紹了建立的轉場動畫都是在轉場動畫上下文UIViewControllerContextTransitioning協定中完成的,那麼這個轉場動畫的執行是誰管理呢?看結構圖如下,沒錯,是由UIViewControllerTransitionCoordinator這個代理協調器在協調器上下文中完成的,系統給UIViewController提供了一個分類,這個分類持有這個代理協調器,通過這個代理協調器可以拿到執行轉場動畫的方法。最終,我們可以自己添加一些操作與轉場動畫同步執行。
UIViewControllerContextTransitioning協定核心内容
@protocol UIViewControllerTransitionCoordinatorContext// 執行的屬性@property(nonatomic, readonly, getter=isAnimated) BOOL animated;
@property(nonatomic, readonly) UIModalPresentationStyle presentationStyle;@property(nonatomic, readonly) NSTimeInterval transitionDuration;@property(nonatomic, readonly) UIView *containerView;
@property(nonatomic, readonly) CGAffineTransform targetTransform
// 參與控制器// UITransitionContextToViewControllerKey、UITransitionContextFromViewControllerKey- (nullable __kindof UIViewController *)viewControllerForKey:(UITransitionContextViewControllerKey)key;// 參與的視圖// UITransitionContextToViewKey、UITransitionContextFromViewKey- (nullable __kindof UIView *)viewForKey:(UITransitionContextViewKey)key API_AVAILABLE(ios(8.0));@end
UIViewControllerTransitionCoordinator協定核心内容
// 與動畫控制器中的轉場動畫同步,執行其他動畫- (BOOL)animateAlongsideTransition:(void (^ __nullable)(id context))animation
completion:(void (^ __nullable)(id context))completion;
// 與動畫控制器中的轉場動畫同步,在指定的視圖内執行動畫- (BOOL)animateAlongsideTransitionInView:(nullable UIView *)view
animation:(void (^ __nullable)(id context))animation
completion:(void (^ __nullable)(id context))completion;
UIViewController(UIViewControllerTransitionCoordinator) 分類核心内容
//持有轉場動畫執行協調器@interface UIViewController(UIViewControllerTransitionCoordinator)
@property(nonatomic, readonly, nullable) id transitionCoordinator;@end
(7)自定義模态風格實作如下【注意:Singleton單例類和UIView+Extesion分類需要自己去拷貝引入】
- 設定UIViewControllerAnimatedTransitioning代理對象TransitionDelegate,監測動畫執行過程并傳回模态風格,将其設定為單例
#import #import "Singleton.h"@interface TransitionDelegate : NSObjectSingletonH(TransitionDelegate);@end
#import "TransitionDelegate.h"#import "CustomPresentationController.h"#import "CustomAnimationTransition.h"@implementation TransitionDelegate SingletonM(TransitionDelegate);#pragma mark -//傳回模态風格-(UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source { return [[CustomPresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting]; }//展示的動畫- (id )animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init]; animation.presented = YES; return animation; }//關閉的動畫- (id )animationControllerForDismissedController:(UIViewController *)dismissed { CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init]; animation.presented = NO; return animation; }@end
-
#import @interface CustomAnimationTransition : NSObject//判斷是present還是dismiss, YES:present NO:dismisss@property (assign,nonatomic)BOOL presented;@end
#import "CustomAnimationTransition.h"#import "UIView+Extension.h"const CGFloat duration = 0.5f;@implementation CustomAnimationTransition#pragma mark -
- 設定自定義的模态風格類CustomPresenttationController
#import "CustomPresentationController.h"@implementation CustomPresentationController//可以改變被模态的控制器視圖的尺寸- (CGRect)frameOfPresentedViewInContainerView { //CGRectInset: 在containerView的frame基礎上,将width減小100,将height減小200 //containerView是容納presentedView的一個容器 return CGRectInset(self.containerView.bounds, 50, 100); }//将上面重置的frame完成布局- (void)containerViewDidLayoutSubviews { self.presentedView.frame = self.frameOfPresentedViewInContainerView; [super containerViewDidLayoutSubviews]; }//過渡即将展示時的處理//這個過程可以改變視圖屬性、或者添加視圖等- (void)presentationTransitionWillBegin { self.presentedView.frame = self.containerView.frame; [self.containerView addSubview:self.presentedView]; }//過渡展示完成//做清理工作- (void)presentationTransitionDidEnd:(BOOL)completed { if (!completed) { [self.presentedView removeFromSuperview]; } }//過渡即将消失時的處理//這個過程可以改變視圖屬性等- (void)dismissalTransitionWillBegin { //例如改變透明度,與轉場控制器中的轉場動畫同步執行 [self.presentingViewController.transitionCoordinator animateAlongsideTransition:^(id
- 開始執行,結果如gif圖
//presentUINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc] init]]; nav.transitioningDelegate = [TransitionDelegate sharedTransitionDelegate];//自定義轉場動畫nav.modalPresentationStyle = UIModalPresentationCustom; //自定義模态風格[self presentViewController:nav animated:YES completion:nil];
iOS:探究視圖控制器的轉場動畫
(8)自定義模态轉場動畫和自定義模态風格我們都實作完了,但是上面的動畫過程中都是不可互動的,那麼要想實作可互動的動畫該怎麼做呢?如上面所說的,在dismiss時傳回一個實作了UIViewControllerInteractiveTransitioning協定的代理或者直接是原生類UIPercentDrivenInteractiveTransition對象。其中,UIPercentDrivenInteractiveTransition是系統封裝好了百分比驅動,用起來很簡單,那麼真正的實作原理還是我們去實作一下。下面咱們來實作導航模式的互動效果,如下:
- TransitioningDelegate
#import
#import "TransitioningDelegate.h"#import "CustomAnimationTransition.h"#import "CustomInteractiveTransition.h"@implementation TransitioningDelegate SingletonM(TransitioningDelegate);#pragma mark - UIViewControllerTransitioningDelegate//present- (nullable id
- CustomAnimationTransition
#import @interface CustomAnimationTransition : NSObject//判斷是present還是dismiss, YES:present NO:dismisss@property (assign,nonatomic)BOOL presented;@end
#import "CustomAnimationTransition.h"#import "UIView+Extension.h"const CGFloat duration = 0.5f;@implementation CustomAnimationTransition#pragma mark -//動畫時間- (NSTimeInterval)transitionDuration:(id )transitionContext { return duration; }//設定過渡動畫(modal和dismiss的動畫都需要在這裡處理)- (void)animateTransition:(id )transitionContext { // UITransitionContextToViewKey, // UITransitionContextFromViewKey. //出來的動畫 if (self.presented) { //擷取并添加轉場視圖 UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey]; [transitionContext.containerView addSubview:toView]; //設定動畫從右往左出來 toView.x = toView.width; [UIView animateWithDuration:duration animations:^{ toView.x = 0; } completion:^(BOOL finished) { //移除視圖 BOOL cancle = [transitionContext transitionWasCancelled]; if (cancle) { [toView removeFromSuperview]; } //動畫完成後,視圖上的事件才能處理 [transitionContext completeTransition:!cancle]; }]; } //銷毀的動畫 else { //不做處理,而是交給自定義的互動動畫去完成 } }@end
- CustomInteractiveTransition
#import #import "Singleton.h"@interface CustomInteractiveTransition : NSObjectSingletonH(CustomInteractiveTransition); //采用單例的方式主要是為了儲存互動上下文//動畫進度更新-(void)updateAnimationProgress:(CGFloat)progress;//動畫完成-(void)finish;//動畫取消-(void)cancel;@end
#import "CustomInteractiveTransition.h"#import "UIView+Extension.h"@interface CustomInteractiveTransition () @property (nonatomic, strong) id
- 在被模态的控制器添加拖拽手勢
#import "SecondViewController.h"#import "CustomInteractiveTransition.h"@interface SecondViewController ()@end@implementation SecondViewController- (void)viewDidLoad { [super viewDidLoad]; self.title = @"secondVc"; self.view.backgroundColor = [UIColor redColor]; [self.view addGestureRecognizer:[[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)]]; }-(void)pan:(UIPanGestureRecognizer *)pan { CGPoint translatedPoint = [pan translationInView:self.view]; CGFloat progress = translatedPoint.x / [UIScreen mainScreen].bounds.size.width; if (progress < 0) { return; } //拖拽的距離進度比 progress = fabs(progress); CustomInteractiveTransition *transition = [[CustomInteractiveTransition alloc] init]; switch (pan.state) { case UIGestureRecognizerStateBegan: [self dismissViewControllerAnimated:YES completion:nil]; break; case UIGestureRecognizerStateChanged: [transition updateAnimationProgress:progress]; break; case UIGestureRecognizerStateEnded: { if (progress > 0.5) { [transition finish]; }else{ [transition cancel]; } break; } default: break; } }@end
-
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc] init]]; nav.transitioningDelegate = [TransitioningDelegate sharedTransitioningDelegate];//自定義可互動轉場動畫nav.modalPresentationStyle = UIModalPresentationFullScreen; //系統模态風格[self presentViewController:nav animated:YES completion:nil];
五、實作一個自定義的導航動畫
1、重寫導航控制器的協定,傳回自定義的導航轉場動畫,動畫實作的方式和modal思想一緻,重寫的核心協定如下:
//重寫導航控制器協定@protocol UINavigationControllerDelegate@optional
................//傳回一個實作了自定義互動動畫的對象- (nullable id )navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id ) animationController;//傳回一個實作了普通動畫的對象- (nullable id )navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC;@end
2、 現在就來自定義一個導航轉場動畫,步驟如下:
- 建立一個CustomNavigationTransition類,實作導航控制器的協定
#import #import "Singleton.h"NS_ASSUME_NONNULL_BEGIN@interface CustomNavigationTransition : NSObjectSingletonH(CustomNavigationTransition);@endNS_ASSUME_NONNULL_END
#import "CustomNavigationTransition.h"#import "CustomNavigationAnimation.h"@implementation CustomNavigationTransition SingletonM(CustomNavigationTransition);#pragma mark - UINavigationControllerDelegate//傳回一個實作了普通動畫的對象- (nullable id )navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { CustomNavigationAnimation *animation = [[CustomNavigationAnimation alloc] init]; animation.operation = operation; return animation; }@end
- 自定義一個CustomNavigationAnimation類,實作動畫協定,重寫動畫效果
#import NS_ASSUME_NONNULL_BEGIN@interface CustomNavigationAnimation : NSObject@property (nonatomic, assign) UINavigationControllerOperation operation;@endNS_ASSUME_NONNULL_END
#import "CustomNavigationAnimation.h"#import "UIView+Extension.h"const CGFloat _duration = 0.5f;@implementation CustomNavigationAnimation#pragma mark -//動畫時間- (NSTimeInterval)transitionDuration:(id )transitionContext { return _duration; }//設定過渡動畫(modal和dismiss的動畫都需要在這裡處理)- (void)animateTransition:(id )transitionContext { // UITransitionContextToViewKey, // UITransitionContextFromViewKey. //push if (self.operation == UINavigationControllerOperationPush) { //轉場視圖 UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey]; [transitionContext.containerView addSubview:toView]; //設定動畫從右上push進來 toView.x = toView.width; toView.y = -toView.height; [UIView animateWithDuration:_duration animations:^{ toView.x = 0; toView.y = 0; } completion:^(BOOL finished) { //移除視圖 BOOL cancle = [transitionContext transitionWasCancelled]; if (cancle) { [toView removeFromSuperview]; } //動畫完成後,視圖上的事件才能處理 [transitionContext completeTransition:!cancle]; }]; } //pop else if(self.operation == UINavigationControllerOperationPop) { //轉場視圖,更改層級關系 UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey]; UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey]; [transitionContext.containerView insertSubview:toView belowSubview:fromView]; [UIView animateWithDuration:_duration animations:^{ //pop傳回右上 fromView.x = fromView.width; fromView.y = -fromView.height; } completion:^(BOOL finished) { //移除視圖 BOOL cancle = [transitionContext transitionWasCancelled]; if (!cancle) { [fromView removeFromSuperview]; } //動畫完成後,視圖上的事件才能處理 [transitionContext completeTransition:!cancle]; }]; } }@end
-
//pushThirdViewController *vc = [[ThirdViewController alloc] init]; self.navigationController.delegate = [CustomNavigationTransition sharedCustomNavigationTransition]; [self.navigationController pushViewController:vc animated:YES];
iOS:探究視圖控制器的轉場動畫
六、實作一個自定義的标簽欄切換動畫
1、重寫标簽欄控制器的協定,傳回自定義的标簽欄切換轉場動畫,動畫實作的方式和modal思想一緻,重寫的核心協定如下:
//重寫标簽欄協定@protocol UITabBarControllerDelegate@optional
......................//傳回一個實作了可互動的标簽欄轉場動畫對象- (nullable id )tabBarController:(UITabBarController *)tabBarController
interactionControllerForAnimationController: (id )animationController;//傳回一個實作了普通的标簽欄轉場動畫對象- (nullable id )tabBarController:(UITabBarController *)tabBarController
animationControllerForTransitionFromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC;@end
2、 現在就來自定義一個标簽轉場動畫,步驟如下:
- 建立一個CustomTabBarViewController類,設定代理CustomTabbarTransition執行個體
//注意:我使用StoryBoard搭建的界面#import NS_ASSUME_NONNULL_BEGIN@interface CustomTabBarViewController : UITabBarController@endNS_ASSUME_NONNULL_END
#import "CustomTabBarViewController.h"#import "CustomTabbarTransition.h"@interface CustomTabBarViewController ()@end@implementation CustomTabBarViewController-(instancetype)initWithCoder:(NSCoder *)coder { if (self = [super initWithCoder:coder]) { //設定代理 self.delegate = [CustomTabbarTransition sharedCustomTabbarTransition]; } return self; }- (void)viewDidLoad { [super viewDidLoad]; }@end
- 建立一個CustomTabbarTransition類,實作标簽欄控制器的協定
#import #import "Singleton.h"NS_ASSUME_NONNULL_BEGIN@interface CustomTabbarTransition : NSObjectSingletonH(CustomTabbarTransition);@endNS_ASSUME_NONNULL_END
#import "CustomTabbarTransition.h"#import "CustomTabbarAnimation.h"@implementation CustomTabbarTransition SingletonM(CustomTabbarTransition);#pragma mark - UITabBarControllerDelegate//傳回一個實作了普通動畫的對象- (nullable id )tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC { CustomTabbarAnimation *animation = [[CustomTabbarAnimation alloc] init]; return animation; }@end
- 建立一個CustomTabbarAnimation類,自定義标簽切換動畫
#import NS_ASSUME_NONNULL_BEGIN@interface CustomTabbarAnimation : NSObject@endNS_ASSUME_NONNULL_END
#import "CustomTabbarAnimation.h"#import "UIView+Extension.h"const CGFloat _Duration = 0.5f;@implementation CustomTabbarAnimation#pragma mark -//動畫時間- (NSTimeInterval)transitionDuration:(id )transitionContext { return _Duration; }//設定過渡動畫(modal和dismiss的動畫都需要在這裡處理)- (void)animateTransition:(id )transitionContext { // UITransitionContextToViewKey, // UITransitionContextFromViewKey. //轉場視圖 UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey]; UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey]; [transitionContext.containerView addSubview:toView]; [transitionContext.containerView sendSubviewToBack:toView]; //尺寸變化,從原尺寸縮小到點 CGRect finalFrame = CGRectInset(transitionContext.containerView.frame, transitionContext.containerView.frame.size.width/2, transitionContext.containerView.frame.size.height/2); [UIView animateWithDuration:_Duration animations:^{ fromView.frame = finalFrame; } completion:^(BOOL finished) { //移除視圖 BOOL cancle = [transitionContext transitionWasCancelled]; if (!cancle) { [fromView removeFromSuperview]; } //動畫完成後,視圖上的事件才能處理 [transitionContext completeTransition:!cancle]; }]; }@end
七、總結