一、介紹 在iOS開發中,轉場動畫的使用無處不見,不只是我們自己更多的使用UIViewblock動畫實現一個轉場動畫,其實,在我們實現VC控制器跳轉的時候都是轉場動畫的實現,例如標簽欄控制器的切換、模態動畫present和dismiss、導航控制器的push和pop。實現它們的轉場動畫,只需要實現它 ...
一、介紹
在iOS開發中,轉場動畫的使用無處不見,不只是我們自己更多的使用UIViewblock動畫實現一個轉場動畫,其實,在我們實現VC控制器跳轉的時候都是轉場動畫的實現,例如標簽欄控制器的切換、模態動畫present和dismiss、導航控制器的push和pop。實現它們的轉場動畫,只需要實現它們的動畫協議即可,說起來有點太籠統,不如看下麵的圖吧:
二、分析
對於上面的三種類型的控制器,系統都會為它們設置一個代理,通過這個代理方法去監測它們切換VC的過程,這個過程僅僅是出現和消失的過程,至於這個過程是什麼過渡效果,這個代理是不管的。要想這個過程是有動畫的,那麼在這些過程中,也就是代理函數中,需要另外再返回一個實現動畫的對象,這個對象必須遵循實現動畫的協議,在這個協議中開發者可以重寫自定義轉場動畫。下麵會慢慢演示這三種類型控制器的自定義轉場動畫。
重寫不可交互轉場動畫的核心協議內容:
//重寫動畫協議 @protocol UIViewControllerAnimatedTransitioning <NSObject> //動畫執行時間 - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext; //自定義動畫效果 - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext; @end
重寫可交互轉場動畫的核心協議內容:
//重寫動畫協議 @protocol UIViewControllerInteractiveTransitioning <NSObject> //自定義動畫效果 - (void)startInteractiveTransition:(id <UIViewControllerContextTransitioning>)transitionContext; @end
系統提供的一個百分比可交互轉場動畫核心類內容:
//系統提供的百分比動畫類,已經遵循了可交互協議 @interface UIPercentDrivenInteractiveTransition : NSObject <UIViewControllerInteractiveTransitioning>
- (void)pauseInteractiveTransition; - (void)updateInteractiveTransition:(CGFloat)percentComplete; - (void)cancelInteractiveTransition; - (void)finishInteractiveTransition; @end
三、轉場動畫View之間的切換
四、實現一個自定義的模態動畫
1、概述
正如我們所知,系統為我們提供的模態動畫預設是從底部present出,然後dismiss回到底部。 雖然說這個基本能夠滿足使用,但是如果我們還想使用其他形式的模態動畫例如從頂部present出dismiss回到頂部,這個時候就需要對系統預設的轉場動畫進行自定義了。
2、詳解
(1)要自定義模態轉場動畫,首先需要給被模態的控制器設置一個實現了UIViewControllerAnimatedTransitioning協議的代理,這些協議方法可以監測動畫執行的過程,代理和協議如下:
//代理 @protocol UIViewControllerTransitioningDelegate; @interface UIViewController(UIViewControllerTransitioning) @property (nullable, nonatomic, weak) id <UIViewControllerTransitioningDelegate> transitioningDelegate API_AVAILABLE(ios(7.0)); @end
//協議
@protocol UIViewControllerTransitioningDelegate <NSObject> @optional //present時調用,返回一個實現了不可交互轉場動畫協議的代理 - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source; //dismiss時調用,返回一個實現了不可交互轉場動畫協議的代理 - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed; //presnt過程中交互時調用,返回一個實現了可交互的轉場動畫協議的代理 - (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator; //dismiss過程中交互時調用,返回一個實現了可交互的轉場動畫協議的代理 - (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator; //返回新的模態彈框控制器(這個是對模態風格進行自定義時調用,後面會說到) - (nullable UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(nullable UIViewController *)presenting sourceViewController:(UIViewController *)source API_AVAILABLE(ios(8.0)); @end
(2)然後在上面的協議方法中返回一個實現了UIViewControllerAnimatedTransitioning協議的代理,在這個代理的協議方法中可以真正重寫轉場動畫了,協議如下:
@protocol UIViewControllerAnimatedTransitioning <NSObject> //動畫執行時間 - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext; //自定義轉場動畫 - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext; @optional
(3)自定義轉場動畫實現如下【註意:Singleton單例類和UIView+Extesion分類需要自己去拷貝引入】
- 設置UIViewControllerAnimatedTransitioning代理對象TransitionDelegate,監測動畫執行過程,將其設置為單例
#import <UIKit/UIKit.h> #import "Singleton.h" @interface TransitionDelegate : NSObject<UIViewControllerTransitioningDelegate> SingletonH(TransitionDelegate); @end
#import "TransitionDelegate.h" #import "CustomAnimationTransition.h" @implementation TransitionDelegate SingletonM(TransitionDelegate); #pragma mark - <UIViewControllerTransitioningDelegate> //展示的動畫 - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init]; animation.presented = YES; return animation; } //關閉的動畫 - (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed { CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init]; animation.presented = NO; return animation; } @end
- 設置UIViewControllerAnimatedTransitioning代理對象CustomTransitionAnimationTransition,重寫動畫效果
#import <UIKit/UIKit.h> @interface CustomAnimationTransition : NSObject<UIViewControllerAnimatedTransitioning> //判斷是present還是dismiss, YES:present NO:dismisss @property (assign,nonatomic)BOOL presented; @end
-
//設置過渡動畫(modal和dismiss的動畫都需要在這裡處理) - (void)animateTransition:(id <UIViewControllerContextTransitioning>)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圖
//present UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc] init]]; nav.transitioningDelegate = [TransitionDelegate sharedTransitionDelegate];//自定義轉場動畫 nav.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:nav animated:YES completion:nil];
(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 <NSObject> // 執行的屬性 @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 <UIViewControllerTransitionCoordinatorContext>context))animation completion:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))completion; // 與動畫控制器中的轉場動畫同步,在指定的視圖內執行動畫 - (BOOL)animateAlongsideTransitionInView:(nullable UIView *)view animation:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))animation completion:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))completion;
UIViewController(UIViewControllerTransitionCoordinator) 分類核心內容
//持有轉場動畫執行協調器 @interface UIViewController(UIViewControllerTransitionCoordinator) @property(nonatomic, readonly, nullable) id <UIViewControllerTransitionCoordinator> transitionCoordinator; @end
(7)自定義模態風格實現如下【註意:Singleton單例類和UIView+Extesion分類需要自己去拷貝引入】
- 設置UIViewControllerAnimatedTransitioning代理對象TransitionDelegate,監測動畫執行過程並返回模態風格,將其設置為單例
#import <UIKit/UIKit.h> #import "Singleton.h" @interface TransitionDelegate : NSObject<UIViewControllerTransitioningDelegate> SingletonH(TransitionDelegate); @end
#import "TransitionDelegate.h" #import "CustomPresentationController.h" #import "CustomAnimationTransition.h" @implementation TransitionDelegate SingletonM(TransitionDelegate); #pragma mark - <UIViewControllerTransitioningDelegate> //返回模態風格 -(UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source { return [[CustomPresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting]; } //展示的動畫 - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init]; animation.presented = YES; return animation; } //關閉的動畫 - (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed { CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init]; animation.presented = NO; return animation; } @end
- 設置UIViewControllerAnimatedTransitioning代理對象CustomTransitionAnimationTransition,重寫動畫效果
#import <UIKit/UIKit.h> @interface CustomAnimationTransition : NSObject<UIViewControllerAnimatedTransitioning> //判斷是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 -<UIViewControllerAnimatedTransitioning> //動畫時間 - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { return duration; } //設置過渡動畫(modal和dismiss的動畫都需要在這裡處理) - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext { // UITransitionContextToViewKey, // UITransitionContextFromViewKey.
//發現此處並沒有添加toView到containerView中以及從containerView中移除toView,與上面的有區別。
//我把添加和移除toView的操作放到了下麵的自定義的模態風格類中完成的
//出來的動畫 if (self.presented) { UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey]; //設置動畫從上往下出來 toView.y = -toView.height; [UIView animateWithDuration:duration animations:^{ toView.y = 0; } completion:^(BOOL finished) { //動畫完成後,視圖上的事件才能處理 [transitionContext completeTransition:YES]; }]; } //銷毀的動畫 else { [UIView animateWithDuration:duration animations:^{ UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey]; fromView.y = -fromView.height; } completion:^(BOOL finished) { //動畫完成後,視圖上的事件才能處理 [transitionContext completeTransition:YES]; }]; } } - 設置自定義的模態風格類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<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { self.presentedView.alpha = 0.f; } completion:nil]; } //過渡消失完成 //做清理工作 - (void)dismissalTransitionDidEnd:(BOOL)completed { if (completed) { [self.presentedView removeFromSuperview]; } } @end - 開始執行,結果如gif圖
//present UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc] init]]; nav.transitioningDelegate = [TransitionDelegate sharedTransitionDelegate];//自定義轉場動畫 nav.modalPresentationStyle = UIModalPresentationCustom; //自定義模態風格 [self presentViewController:nav animated:YES completion:nil];
(8)自定義模態轉場動畫和自定義模態風格我們都實現完了,但是上面的動畫過程中都是不可交互的,那麼要想實現可交互的動畫該怎麼做呢?如上面所說的,在dismiss時返回一個實現了UIViewControllerInteractiveTransitioning協議的代理或者直接是原生類UIPercentDrivenInteractiveTransition對象。其中,UIPercentDrivenInteractiveTransition是系統封裝好了百分比驅動,用起來很簡單,那麼真正的實現原理還是我們去實現一下。下麵咱們來實現導航模式的交互效果,如下:
- TransitioningDelegate
#import <UIKit/UIKit.h> #import "Singleton.h"
@interface TransitioningDelegate : NSObject<UIViewControllerTransitioningDelegate> SingletonH(TransitioningDelegate); @end#import "TransitioningDelegate.h" #import "CustomAnimationTransition.h" #import "CustomInteractiveTransition.h" @implementation TransitioningDelegate SingletonM(TransitioningDelegate); #pragma mark - UIViewControllerTransitioningDelegate //present - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source { //這裡還是採用自定義的轉場動畫方式進行present,使其present時從屏幕右側滑入
CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init]; animation.presented = YES; return animation; } //dismiss,必須重寫 - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed { //這裡採用自定義的轉場動畫覆蓋系統的dismiss效果,在dismiss時,由於自定義了交互動畫,所以系統自己的dismiss動畫不會執行
CustomAnimationTransition *animation = [[CustomAnimationTransition alloc] init]; animation.presented = NO; return animation; } //將要dismiss時的交互行為,必須重寫 - (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator { CustomInteractiveTransition *animation = [[CustomInteractiveTransition alloc] init]; return animation; } @end - CustomAnimationTransition
#import <UIKit/UIKit.h> @interface CustomAnimationTransition : NSObject<UIViewControllerAnimatedTransitioning> //判斷是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 -<UIViewControllerAnimatedTransitioning> //動畫時間 - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext { return duration; } //設置過渡動畫(modal和dismiss的動畫都需要在這裡處理) - (void)animateTransition:(id <UIViewControllerContextTransitioning>)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 <UIKit/UIKit.h> #import "Singleton.h" @interface CustomInteractiveTransition : NSObject<UIViewControllerInteractiveTransitioning> SingletonH(CustomInteractiveTransition); //採用單例的方式主要是為了保存交互上下文 //動畫進度更新 -(void)updateAnimationProgress:(CGFloat)progress; //動畫完成 -(void)finish; //動畫取消 -(void)cancel; @end
#import "CustomInteractiveTransition.h" #import "UIView+Extension.h" @interface CustomInteractiveTransition () @property (nonatomic, strong) id<UIViewControllerContextTransitioning> context; @end @implementation CustomInteractiveTransition SingletonM(CustomInteractiveTransition); #pragma mark - UIViewControllerInteractiveTransitioning //開始交互時調用 - (void)startInteractiveTransition:(id <UIViewControllerContextTransitioning>)transitionContext { //保存上下文 self.context = transitionContext; //更改視圖層級 UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey]; UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey]; [transitionContext.containerView insertSubview:toView belowSubview:fromView]; } //動畫進度更新 -(void)updateAnimationProgress:(CGFloat)progress { UIView *fromView = [self.context viewForKey:UITransitionContextFromViewKey]; fromView.x = self.context.containerView.width * progress; } //動畫完成 -(void)finish {
UIView *fromView = [self.context viewForKey:UITransitionContextFromViewKey]; [UIView animateWithDuration:0.2 animations:^{ fromView.x += self.context.containerView.width; } completion:^(BOOL finished) {
[fromView removeFromSuperView];
[self.context completeTransition:finished]; }]; } //動畫取消 -(void)cancel {
UIView *fromView = [self.context viewForKey:UITransitionContextFromViewKey]; [UIView animateWithDuration:0.2 animations:^{ fromView.x = 0; } completion:^(BOOL finished) { [fromView removeFromSuperView];
[self.context cancelInteractiveTransition]; }]; } @end - 在被模態的控制器添加拖拽手勢
#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
- 開始執行,結果如gif圖
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 <NSObject> @optional ................ //返回一個實現了自定義交互動畫的對象 - (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController; //返回一個實現了普通動畫的對象 - (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC; @end
2、 現在就來自定義一個導航轉場動畫,步驟如下:
- 創建一個CustomNavigationTransition類,實現導航控制器的協議
#import <UIKit/UIKit.h> #import "Singleton.h" NS_ASSUME_NONNULL_BEGIN @interface CustomNavigationTransition : NSObject<UINavigationControllerDelegate> SingletonH(CustomNavigationTransition); @end NS_ASSUME_NONNULL_END
#import "CustomNavigationTransition.h" #import "CustomNavigationAnimation.h" @implementation CustomNavigationTransition SingletonM(CustomNavigationTransition); #pragma mark - UINavigationControllerDelegate //返回一個實現了普通動畫的對象 - (nullable id <UIViewControllerAnimatedTransitioning>)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 <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN @inte