iOS: explore transition animation of view controller

I. Introduction

In iOS development, the use of transition animation is everywhere, not only our own use of UIViewblock animation to achieve a transition animation, in fact, when we realize the jump of VC controller, it is the realization of transition animation, such as the switch of label bar controller, the present ation and distribution of model animation, the push and pop of navigation controller. To realize their transition animation, you only need to implement their animation protocol, which is a bit too general. Let's see the following figure:

 

Two, analysis

For the above three types of controllers, the system will set an agent for them to monitor the process of switching VC through this agent method. This process is only the process of appearance and disappearance. As for the transition effect of this process, this agent does not care. In order to have animation in this process, we need to return another animation object in these processes, that is, the proxy function. This object must follow the animation protocol, in which the developer can rewrite the custom transition animation. The following will slowly demonstrate the custom transition animation of these three types of controllers.

Rewrite the core protocol content of non interactive transition Animation:

//Rewrite animation protocol
@protocol UIViewControllerAnimatedTransitioning <NSObject>

//Animation execution time
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;

//Custom animation effects
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;

@end

Rewrite the core protocol content of interactive transition Animation:

//Rewrite animation protocol
@protocol UIViewControllerInteractiveTransitioning <NSObject>

//Custom animation effects
- (void)startInteractiveTransition:(id <UIViewControllerContextTransitioning>)transitionContext;

@end

The system provides a percentage of interactive transition animation core class content:

//The percentage animation class provided by the system has followed the interactive protocol
@interface UIPercentDrivenInteractiveTransition : NSObject <UIViewControllerInteractiveTransitioning>
- (void)pauseInteractiveTransition; - (void)updateInteractiveTransition:(CGFloat)percentComplete; - (void)cancelInteractiveTransition; - (void)finishInteractiveTransition; @end

 

III. switching between transition animation views

 

IV. realize a customized modal animation

1. Overview

As we all know, the mode animation provided by the system is presented from the bottom by default, and then dismiss returns to the bottom Although this is basically enough for use, if we want to use other forms of modal animation, such as from the top present to the top, then we need to customize the default transition animation of the system.

2, detailed explanation

(1) to customize the mode transition animation, first set a proxy that implements uiviewcontrolleranimatedtransition protocol for the modal controller. These protocol methods can monitor the execution process of the animation. The proxy and protocol are as follows:

//agent
@protocol UIViewControllerTransitioningDelegate;
@interface UIViewController(UIViewControllerTransitioning)
@property (nullable, nonatomic, weak) id <UIViewControllerTransitioningDelegate> transitioningDelegate API_AVAILABLE(ios(7.0));
@end
//Agreement
@protocol
UIViewControllerTransitioningDelegate <NSObject> @optional //present When called, returns a proxy that implements the non interactive transition animation protocol - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source; //dismiss When called, returns a proxy that implements the non interactive transition animation protocol - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed; //presnt It is called when interacting in the process, and returns a proxy that implements the interactive transition animation protocol - (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForPresentation:(id <UIViewControllerAnimatedTransitioning>)animator; //dismiss It is called when interacting in the process, and returns a proxy that implements the interactive transition animation protocol - (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator; //Return to the new modal spring box controller (this is called when the modal style is customized, which will be mentioned later) - (nullable UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(nullable UIViewController *)presenting sourceViewController:(UIViewController *)source API_AVAILABLE(ios(8.0)); @end

(2) then return a proxy that implements uiviewcontrolleranimatedtransition protocol in the above protocol method. In this proxy's protocol method, the transition animation can be rewritten. The protocol is as follows:

@protocol UIViewControllerAnimatedTransitioning <NSObject>
//Animation execution time
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
//Custom transition animation
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
@optional

(3) the implementation of user-defined transition animation is as follows [Note: Singleton singleton class and uiview + extension classification need to be copied and imported by yourself]

  • Set the UIViewControllerAnimatedTransitioning proxy object, TransitionDelegate, monitor the execution process of the animation, and set it as a single example
    #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>
    
    //Animation shown
    - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
    {
        CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init];
        animation.presented = YES;
        return animation;
    }
    
    //Closed animation
    - (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
    {
        CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init];
        animation.presented = NO;
        return animation;
    }
    @end
  • Set the uiviewcontrolleranimatedtransition proxy object CustomTransitionAnimationTransition to override the animation effect
    #import <UIKit/UIKit.h>
    
    @interface CustomAnimationTransition : NSObject<UIViewControllerAnimatedTransitioning>
    //Judgement is present still dismiss, YES:present  NO:dismisss
    @property (assign,nonatomic)BOOL presented;
    @end
  • //Animating transitions( modal and dismiss All animations need to be processed here.)
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
    {
        // UITransitionContextToViewKey,
        // UITransitionContextFromViewKey.
        
        //Animation coming out
        if (self.presented) {
            
            //Get and add transition view
            UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
            [transitionContext.containerView addSubview:toView];
             
            //Animate from top to bottom
            toView.y = -toView.height;
            
            [UIView animateWithDuration:duration animations:^{
                
                toView.y = 0;
                
            } completion:^(BOOL finished) {
                
                //Remove View
                BOOL cancle = [transitionContext transitionWasCancelled];
                if (cancle) {
                    [toView removeFromSuperview];
                }
                
                //After animation,Events on view can be processed
                [transitionContext completeTransition:!cancle];
            }];
        }
        //Destroyed animation
        else
        {
            //Get transition view
            UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
            
            [UIView animateWithDuration:duration animations:^{
                
                fromView.y = -fromView.height;
    
            } completion:^(BOOL finished) {
                
                //Remove View
                BOOL cancle = [transitionContext transitionWasCancelled];
                if (!cancle) {
                    [fromView removeFromSuperview];
                }
                
                //After animation,Events on view can be processed
                [transitionContext completeTransition:!cancle];
            }];
        }
    }
  • Start execution, the result is as shown in gif figure
    //present
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc] init]];
    nav.transitioningDelegate = [TransitionDelegate sharedTransitionDelegate];//Custom transition animation
    nav.modalPresentationStyle = UIModalPresentationFullScreen;
    [self presentViewController:nav animated:YES completion:nil];

(4) we have implemented a simple user-defined mode non interactive transition animation. In fact, when we use the mode controller, we can also customize the interactive transition animation and set the user-defined mode style. Interactive transition animation will be discussed later. First, we will discuss the mode style. Before IOS 13, the system default is uimodal presentation fullscreen in full screen mode, but after IOS 13, the default is uimodal presentation page sheet. The mode styles provided by the system are as follows:

//Modal style enumeration
typedef NS_ENUM(NSInteger, UIModalPresentationStyle) {
    UIModalPresentationFullScreen = 0,
    UIModalPresentationPageSheet ,
    UIModalPresentationFormSheet ,
    UIModalPresentationCurrentContext ,
    UIModalPresentationCustom , //custom
    UIModalPresentationOverFullScreen ,
    UIModalPresentationOverCurrentContext ),
    UIModalPresentationPopover ,
    UIModalPresentationBlurOverFullScreen ,
    UIModalPresentationNone,
    UIModalPresentationAutomatic , 
};

(5) from the above enumeration, we can see that the system supports our own style, that is, customization. Before implementing customization, you must know the UIPresentationController class. This is a pop-up control. The modal controllers are managed by it. The main code is as follows:

//Override this method to perform the required actions when the pop-up box is about to display
- (void)presentationTransitionWillBegin;
//Override this method to perform the required actions when the pop-up box is finished - (void)presentationTransitionDidEnd:(BOOL)completed;
//Override this method to perform the required actions when the pop-up box is about to disappear - (void)dismissalTransitionWillBegin;
//Override this method to perform the required actions after the bullet box disappears - (void)dismissalTransitionDidEnd:(BOOL)completed;
//Override determines the frame - (CGRect)frameOfPresentedViewInContainerView;
//RewriteYes containerView Layout - (void)containerViewWillLayoutSubviews; - (void)containerViewDidLayoutSubviews;
//Initialization method - (instancetype)initWithPresentedViewController:(UIViewController *)presentedViewController presentingViewController:(UIViewController *)presentingViewController;

(6) another knowledge point will be mentioned, because it will be involved in the custom mode style later. In the structure chart at the beginning of this article, it is introduced that the transition animation created is completed in the transition animation context uiviewcontrollercontexttransition protocol, so who manages the execution of the transition animation? The structure chart is as follows. Yes, it is completed by the agent coordinator uiviewcontrollertransition coordinator in the context of the coordinator. The system provides a classification for UIViewController, which holds the agent coordinator. Through the agent coordinator, you can get the method of executing transition animation. Finally, we can add some operations to synchronize with the transition animation.

 

UIViewControllerContextTransitioning protocol core content

@protocol UIViewControllerTransitionCoordinatorContext <NSObject>
// Properties executed
@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 

// Participating controller
// UITransitionContextToViewControllerKey,UITransitionContextFromViewControllerKey
- (nullable __kindof UIViewController *)viewControllerForKey:(UITransitionContextViewControllerKey)key;

// Participating views
// UITransitionContextToViewKey,UITransitionContextFromViewKey
- (nullable __kindof UIView *)viewForKey:(UITransitionContextViewKey)key API_AVAILABLE(ios(8.0));
@end

Core content of uiviewcontrollertransition coordinator protocol

// Synchronize with transition animation in animation controller to perform other animations
- (BOOL)animateAlongsideTransition:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))animation
                        completion:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))completion;
 
// Synchronize with the transition animation in the animation controller to execute the animation in the specified view
- (BOOL)animateAlongsideTransitionInView:(nullable UIView *)view
                               animation:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))animation
                              completion:(void (^ __nullable)(id <UIViewControllerTransitionCoordinatorContext>context))completion;

Uiviewcontroller (uiviewcontrollertransition Coordinator) classification core content

//Hold transition animation Execution Coordinator
@interface UIViewController(UIViewControllerTransitionCoordinator)
@property(nonatomic, readonly, nullable) id <UIViewControllerTransitionCoordinator> transitionCoordinator;
@end

(7) the implementation of user-defined modal style is as follows [Note: Singleton singleton class and uiview + extension classification need to be copied and imported by themselves]

  • Set UIViewControllerAnimatedTransitioning proxy object TransitionDelegate, monitor the execution process of the animation and return to the mode style, and set it as a single example
    #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>
    //Return to modal style
    -(UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source
    {
        return [[CustomPresentationController alloc] initWithPresentedViewController:presented presentingViewController:presenting];
    }
    
    //Animation shown
    - (id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
    {
        CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init];
        animation.presented = YES;
        return animation;
    }
    
    //Closed animation
    - (id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
    {
        CustomAnimationTransition *animation = [[CustomAnimationTransition alloc]init];
        animation.presented = NO;
        return animation;
    }
    @end
  • Set the uiviewcontrolleranimatedtransition proxy object CustomTransitionAnimationTransition to override the animation effect
    #import <UIKit/UIKit.h>
    
    @interface CustomAnimationTransition : NSObject<UIViewControllerAnimatedTransitioning>
    //Judgement is present still 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>
    //Animation time
    - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
    {
        return duration;
    }
    
    //Animating transitions( modal and dismiss All animations need to be processed here.)
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
    {
        // UITransitionContextToViewKey,
        // UITransitionContextFromViewKey.    
        
       //Now there is no difference between adding toView to containerView and removing toView from containerView.
    //I put the operation of adding and removing toView into the following custom modal style class

    //
    Animation coming out if (self.presented) { UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey]; //Animate from top to bottom toView.y = -toView.height; [UIView animateWithDuration:duration animations:^{ toView.y = 0; } completion:^(BOOL finished) { //After animation,Events on view can be processed [transitionContext completeTransition:YES]; }]; } //Destroyed animation else { [UIView animateWithDuration:duration animations:^{ UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey]; fromView.y = -fromView.height; } completion:^(BOOL finished) { //After animation,Events on view can be processed [transitionContext completeTransition:YES]; }]; } }
  • Set custom mode style class custompresentationcontroller
    #import "CustomPresentationController.h"
    
    @implementation CustomPresentationController
    
    //Can change the size of the view of the modal controller
    - (CGRect)frameOfPresentedViewInContainerView
    {
       //CGRectInset: stay containerView Of frame On the basis of width Decrease by 100 height Reduce 200
    //containerView Is to accommodate presentedView A container of return CGRectInset(self.containerView.bounds, 50, 100); } //Reset the frame Finish layout - (void)containerViewDidLayoutSubviews { self.presentedView.frame = self.frameOfPresentedViewInContainerView; [super containerViewDidLayoutSubviews]; } //How to handle the transition when it is about to be displayed //This process can change view properties, or add views, etc - (void)presentationTransitionWillBegin { self.presentedView.frame = self.containerView.frame; [self.containerView addSubview:self.presentedView]; } //Transition display complete //Clean up - (void)presentationTransitionDidEnd:(BOOL)completed { if (!completed) { [self.presentedView removeFromSuperview]; } } //Handling when transition is about to disappear //This process can change view properties, etc - (void)dismissalTransitionWillBegin { //For example, change the transparency and execute synchronously with the transition animation in the transition controller [self.presentingViewController.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) { self.presentedView.alpha = 0.f; } completion:nil]; } //Transition disappear complete //Clean up - (void)dismissalTransitionDidEnd:(BOOL)completed { if (completed) { [self.presentedView removeFromSuperview]; } } @end
  • Start execution, the result is as shown in gif figure
    //present
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc] init]];
    nav.transitioningDelegate = [TransitionDelegate sharedTransitionDelegate];//Custom transition animation
    nav.modalPresentationStyle = UIModalPresentationCustom; //Custom modal style
    [self presentViewController:nav animated:YES completion:nil];

(8) we have completed the implementation of custom mode transition animation and custom mode style, but the above animation process is not interactive, so how to achieve interactive animation? As mentioned above, a proxy that implements uiviewcontrollerinteractivetransition protocol or a native UIPercentDrivenInteractiveTransition object is returned during dispatch. Among them, UIPercentDrivenInteractiveTransition is that the system encapsulates the percentage driver, which is very simple to use. So the real implementation principle is to implement it. Let's realize the interactive effect of navigation mode as follows:

  • 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 {
        
         //Here, the user-defined transition animation is used to present, so that it slides in from the right side of the screen
    CustomAnimationTransition
    *animation = [[CustomAnimationTransition alloc]init]; animation.presented = YES; return animation; } //dismiss,Must rewrite - (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed { //Here, the custom transition animation is used to cover the dismission effect of the system. When dismissing, because the interactive animation is customized, the system's own dismission animation will not be executed
    CustomAnimationTransition
    *animation = [[CustomAnimationTransition alloc] init]; animation.presented = NO; return animation; } //Will dismiss Must override - (nullable id <UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id <UIViewControllerAnimatedTransitioning>)animator { CustomInteractiveTransition *animation = [[CustomInteractiveTransition alloc] init]; return animation; } @end
  • CustomAnimationTransition
    #import <UIKit/UIKit.h>
    
    @interface CustomAnimationTransition : NSObject<UIViewControllerAnimatedTransitioning>
    //Judgement is present still 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>
    //Animation time
    - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
    {
        return duration;
    }
    
    //Animating transitions( modal and dismiss All animations need to be processed here.)
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
    {
        // UITransitionContextToViewKey,
        // UITransitionContextFromViewKey.
        
        //Animation coming out
        if (self.presented) {
            
            //Get and add transition view
            UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
            [transitionContext.containerView addSubview:toView];
             
            //Animate right to left
            toView.x = toView.width;
    
            [UIView animateWithDuration:duration animations:^{
    
                toView.x = 0;
    
            } completion:^(BOOL finished) {
                
                //Remove View
                BOOL cancle = [transitionContext transitionWasCancelled];
                if (cancle) {
                    [toView removeFromSuperview];
                }
                
                //After animation,Events on view can be processed
                [transitionContext completeTransition:!cancle];
            }];
        }
        //Destroyed animation
        else
        {
            //Instead of processing, it's left to the user-defined interactive animation to complete
        }
    }
    @end
  • CustomInteractiveTransition
    #import <UIKit/UIKit.h>
    #import "Singleton.h"
    
    @interface CustomInteractiveTransition : NSObject<UIViewControllerInteractiveTransitioning>
    SingletonH(CustomInteractiveTransition); //The single example method is mainly used to save the interaction context
    
    //Animation progress update
    -(void)updateAnimationProgress:(CGFloat)progress;
    
    //Animation completed
    -(void)finish;
    
    //Animation cancelled
    -(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
    
    //Called at the beginning of an interaction
    - (void)startInteractiveTransition:(id <UIViewControllerContextTransitioning>)transitionContext {
        
        //Save context
        self.context = transitionContext;
        
        //Change view level
        UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
        UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
        [transitionContext.containerView insertSubview:toView belowSubview:fromView];
    }
    
    
    //Animation progress update
    -(void)updateAnimationProgress:(CGFloat)progress {
        
        UIView *fromView = [self.context viewForKey:UITransitionContextFromViewKey];
        fromView.x = self.context.containerView.width * progress;
        
    }
    
    //Animation completed
    -(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]; }]; }
    //Animation cancelled -(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
  • Add drag gestures to the modal controller
    #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;
        }
        //Drag distance progress ratio
        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
  • Start execution, the result is as shown in gif figure
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:[[SecondViewController alloc] init]];
    nav.transitioningDelegate = [TransitioningDelegate sharedTransitioningDelegate];//Custom interactive transition animation
    nav.modalPresentationStyle = UIModalPresentationFullScreen; //System modal style
    [self presentViewController:nav animated:YES completion:nil];  

 

5. Implement a custom navigation animation

1. Rewrite the protocol of the navigation controller and return the customized navigation transition animation. The animation is implemented in the same way as the modal idea, without screenshots. The core protocol of the rewrite is as follows:

//Override navigation controller protocol
@protocol UINavigationControllerDelegate <NSObject>
@optional
................

//Returns an object that implements a custom interactive animation
- (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
                          interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController;


//Returns an object that implements normal animation
- (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
                                   animationControllerForOperation:(UINavigationControllerOperation)operation
                                                fromViewController:(UIViewController *)fromVC
                                                  toViewController:(UIViewController *)toVC;
@end

2. Now you can define a navigation transition animation. The steps are as follows:

  • Create a CustomNavigationTransition class to implement the protocol of the navigation controller
    #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
    
    //Returns an object that implements normal animation
    - (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
  • Customize a CustomNavigationAnimation class to implement the animation protocol and rewrite the animation effect
    #import <UIKit/UIKit.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface CustomNavigationAnimation : NSObject<UIViewControllerAnimatedTransitioning>
    @property (nonatomic, assign) UINavigationControllerOperation operation;
    @end
    
    NS_ASSUME_NONNULL_END
    #import "CustomNavigationAnimation.h"
    #import "UIView+Extension.h"
    
    const CGFloat _duration = 0.5f;
    
    @implementation CustomNavigationAnimation
    
    #pragma mark -<UIViewControllerAnimatedTransitioning>
    //Animation time
    - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
    {
        return _duration;
    }
    
    //Animating transitions( modal and dismiss All animations need to be processed here.)
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
    {
        // UITransitionContextToViewKey,
        // UITransitionContextFromViewKey.
        
        //push
        if (self.operation == UINavigationControllerOperationPush) {
            
            //Transition view
            UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
            [transitionContext.containerView addSubview:toView];
             
            //Animate from top right push come in
            toView.x = toView.width;
            toView.y = -toView.height;
            
            [UIView animateWithDuration:_duration animations:^{
    
                toView.x = 0;
                toView.y = 0;
                
            } completion:^(BOOL finished) {
                
                //Remove View
                BOOL cancle = [transitionContext transitionWasCancelled];
                if (cancle) {
                    [toView removeFromSuperview];
                }
                
                //After animation,Events on view can be processed
                [transitionContext completeTransition:!cancle];
            }];
        }
        //pop
        else if(self.operation == UINavigationControllerOperationPop)
        {
            //Transition view, change hierarchy
            UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
            UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
            [transitionContext.containerView insertSubview:toView belowSubview:fromView];
    
            [UIView animateWithDuration:_duration animations:^{
                
                //pop Return to upper right
                fromView.x = fromView.width;
                fromView.y = -fromView.height;
                
            } completion:^(BOOL finished) {
                
                //Remove View
                BOOL cancle = [transitionContext transitionWasCancelled];
                if (!cancle) {
                    [fromView removeFromSuperview];
                }
                
                //After animation,Events on view can be processed
                [transitionContext completeTransition:!cancle];
            }];
        }
    }
    @end
  • Start execution, the result is as shown in gif figure
    //push
    ThirdViewController *vc = [[ThirdViewController alloc] init];
    self.navigationController.delegate = [CustomNavigationTransition sharedCustomNavigationTransition];
    [self.navigationController pushViewController:vc animated:YES];


Vi. implement a customized tab bar switching animation

1. Rewrite the protocol of the label bar controller and return the customized label bar switch transition animation. The animation is implemented in the same way as the modal idea, so the screenshot is not needed. The core protocol of the rewrite is as follows:

//Rewrite tabbed protocol
@protocol UITabBarControllerDelegate <NSObject>
@optional
......................

//Returns an interactive tab bar transition animation object
- (nullable id <UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController
                      interactionControllerForAnimationController: (id <UIViewControllerAnimatedTransitioning>)animationController;

//Returns an animation object that implements the normal tabbed transition
- (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
            animationControllerForTransitionFromViewController:(UIViewController *)fromVC
                                              toViewController:(UIViewController *)toVC;

@end

2. Now, define a label transition animation. The steps are as follows:

  • Create a CustomTabBarViewController class and set the agent CustomTabbarTransition instance
    //Note: I use StoryBoard Built interface
    #import <UIKit/UIKit.h>
    NS_ASSUME_NONNULL_BEGIN
    @interface CustomTabBarViewController : UITabBarController
    @end
    NS_ASSUME_NONNULL_END
    #import "CustomTabBarViewController.h"
    #import "CustomTabbarTransition.h"
    
    @interface CustomTabBarViewController ()
    
    @end
    
    @implementation CustomTabBarViewController
    
    -(instancetype)initWithCoder:(NSCoder *)coder {
        if (self = [super initWithCoder:coder]) {
            //Setting agent
            self.delegate = [CustomTabbarTransition sharedCustomTabbarTransition];
        }
        return self;
    }
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
    }
    @end
  • Create a CustomTabbarTransition class to implement the protocol of the tabbar controller
    #import <UIKit/UIKit.h>
    #import "Singleton.h"
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface CustomTabbarTransition : NSObject<UITabBarControllerDelegate>
    SingletonH(CustomTabbarTransition);
    @end
    
    NS_ASSUME_NONNULL_END
    #import "CustomTabbarTransition.h"
    #import "CustomTabbarAnimation.h"
    
    @implementation CustomTabbarTransition
    SingletonM(CustomTabbarTransition);
    
    #pragma mark - UITabBarControllerDelegate
    //Returns an object that implements normal animation
    - (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
    animationControllerForTransitionFromViewController:(UIViewController *)fromVC
                                                           toViewController:(UIViewController *)toVC {
        
        CustomTabbarAnimation *animation = [[CustomTabbarAnimation alloc] init];
        return animation;
    }
    
    @end
  • Create a CustomTabbarAnimation class to customize the label switching animation
    #import <UIKit/UIKit.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface CustomTabbarAnimation : NSObject<UIViewControllerAnimatedTransitioning>
    
    @end
    
    NS_ASSUME_NONNULL_END
    #import "CustomTabbarAnimation.h"
    #import "UIView+Extension.h"
    
    const CGFloat _Duration = 0.5f;
    
    @implementation CustomTabbarAnimation
    
    #pragma mark -<UIViewControllerAnimatedTransitioning>
    //Animation time
    - (NSTimeInterval)transitionDuration:(id <UIViewControllerContextTransitioning>)transitionContext
    {
        return _Duration;
    }
    
    //Animating transitions( modal and dismiss All animations need to be processed here.)
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
    {
        // UITransitionContextToViewKey,
        // UITransitionContextFromViewKey.
        
        //Transition view
        UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey];
        UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
        [transitionContext.containerView addSubview:toView];
        [transitionContext.containerView sendSubviewToBack:toView];
        
        //Dimensional change,Reduce from original size to point
        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) {
                   
                   //Remove View
                   BOOL cancle = [transitionContext transitionWasCancelled];
                   if (!cancle) {
                       [fromView removeFromSuperview];
                   }
                   
                   //After animation,Events on view can be processed
                   [transitionContext completeTransition:!cancle];
               }];
    }
    @end
  • Start execution, the result is as shown in gif figure

 

 

Seven, summary

Well, these three commonly used transition animations have been customized. Of course, as for the latter two kinds of interactive transition animations, the same as the realization principle of modal is not introduced. Borrow and modify a diagram of others and make a summary as follows:

Keywords: iOS Spring

Added by shivangp on Sat, 09 Nov 2019 13:46:08 +0200