Effect:

Set mode customization

  1. UIModalPresentationStyle is used to customize transitions when setting modes.
typedefNS_ENUM(NSInteger.UIModalPresentationStyle) {
      UIModalPresentationFullScreen =0.// Full screen coverage from bottom to top
      UIModalPresentationPageSheet.// In portrait it is FullScreen, in landscape it is FormSheet.
      UIModalPresentationFormSheet.// Shrinks the window to the middle of the screen. It's the same in Portrait and landscape, but note that the window position will change if the soft keyboard appears in landscape.
      UIModalPresentationCurrentContext.// In this mode, the Presenting VC presented in the same manner as the presenting VC's parent.
      UIModalPresentationCustom.// Custom view presentation style, consisting of a custom presentation controller and one or more custom animation objects. In accordance with UIViewControllerTransitioningDelegate agreement. Use the view controller's transitioningDelegate to set your custom transformation.
      UIModalPresentationOverFullScreen.// If the view is not filled, the underlying view can pass through
      UIModalPresentationOverCurrentContext.// All views are passed through
      UIModalPresentationPopover.UIModalPresentationNone};Copy the code
  1. We set up custom modes and proxies in the ViewController.
  // Set the proxy and present style
     self.transitioningDelegate = self;
     self.modalPresentationStyle = UIModalPresentationCustom;
     
Copy the code
  1. Add a gesture to slide the previous page
    // Add gestures
    UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc] init];
    pan.delegate = self;
    [pan addTarget:self action:@selector(panGestureRecognizerAction:)];
    self.view.tag = 10001;
    [self.view addGestureRecognizer:pan];
Copy the code
  1. Gestures handle sliding logic
- (void)panGestureRecognizerAction:(UIPanGestureRecognizer *)pan
{
    // Generate percentage
    CGFloat process = ([pan translationInView:self.view].y) / ([UIScreen mainScreen].bounds.size.height);

    process = MIN(1.0, (MAX(0.0, process)));
    
    // Start sliding
    if (pan.state == UIGestureRecognizerStateBegan)
    {
        self.interactiveTransition = [UIPercentDrivenInteractiveTransition new];
        // Trigger the dismiss animation
        [self dismissViewControllerAnimated:YES completion:nil];
        
    }
    // When the gesture slides
    else if (pan.state == UIGestureRecognizerStateChanged) {[self.interactiveTransition updateInteractiveTransition:process];
    }
    // Swipe to end or cancel
    else if (pan.state == UIGestureRecognizerStateEnded
             || pan.state == UIGestureRecognizerStateCancelled)
    {
        // Slide percentage greater than 0.3 to complete the animation
        if (process >= 0.3) {[self.interactiveTransition finishInteractiveTransition];
        }
        else{[self.interactiveTransition cancelInteractiveTransition];
        }
        self.interactiveTransition = nil; }}Copy the code
  1. Implement proxy UIViewControllerTransitioningDelegate
#pragma mark - UIViewControllerTransitioningDelegate
// Sets the properties of the custom class that inherits from UIPresentationController
- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented presentingViewController:(UIViewController *)presenting sourceViewController:(UIViewController *)source
{
    
    ZJPresentationController *presentVC = [[ZJPresentationController alloc]initWithPresentedViewController:presented presentingViewController:presenting];
    // Set the height from the top
    presentVC.height = STATUSBAR_H;
    
    return presentVC;
}

/ / controller please create animation (returning an implementation class UIViewControllerAnimatedTransitioning agreement)
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source{
    
    / / create the implementation UIViewControllerAnimatedTransitioning agreement (named AnimatedTransitioning)
    ZJPresentAnimation *animation = [[ZJPresentAnimation alloc] init];
    
    // Change its state to appear
    animation.presented = YES;
    
    return animation;
}

/ / controller destruction of execution of the animation (returning an implementation class UIViewControllerAnimatedTransitioning agreement)
- (nullable id <UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed{
    
    / / / / to create UIViewControllerAnimatedTransitioning agreement class (named AnimatedTransitioning)
    ZJPresentAnimation *animation = [[ZJPresentAnimation alloc] init];
    
    // Change its state to appear
    animation.presented = NO;
    
    return animation;
}

/ / returns an interactive object (achieve UIViewControllerInteractiveTransitioning agreement)
-(id<UIViewControllerInteractiveTransitioning>)interactionControllerForDismissal:(id<UIViewControllerAnimatedTransitioning>)animator{
    return self.interactiveTransition;
}
Copy the code

Custom transitions to animation

  1. Implement UIViewControllerAnimatedTransitioning agreement, contains the animation time and specific implementation.
// Customize the transition animation time
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext{
    return 0.34f;
}


// Custom convert to animation concrete implementation
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext{
    
    // To which vc
    UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
    
    // From which vc
    UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
    
    // Get the transition container view
    UIView *contentView  = [transitionContext containerView];;
    
    
    // Remove the previous one
    for (UIView *subView in [contentView subviews]) {
        if (subView.tag == 1001) { [subView removeFromSuperview]; }}UIView * transView = nil;
    // Present
    if (_presented) {
        // Record the present view
        transView = toViewController.view;
        
    }
    else {
        transView = fromViewController.view;
    }
    
    / / y value
    CGFloat y = transView.frame.origin.y;
    transView.frame = CGRectMake(0, _presented ?SCREEN_H :y, SCREEN_W.SCREEN_H);
    
    // Black background
    UIView  *viewCover = [[UIView alloc]initWithFrame:CGRectMake(0.0.SCREEN_W.SCREEN_H)];
    viewCover.backgroundColor = [UIColor blackColor];
    viewCover.alpha = 0.5;
    viewCover.tag = 1001;
    [contentView insertSubview:viewCover belowSubview:transView];
    
    if (_presented) {
        
        // Execute the animation process
        [UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:2 options:UIViewAnimationOptionLayoutSubviews animations:^{
            transView.frame = CGRectMake(0, y , SCREEN_W.SCREEN_H);
        } completion:^(BOOL finished) {
            [transitionContext completeTransition:!transitionContext.transitionWasCancelled];
        }];
        
    }
    else
    {
        // Pull down the drawing
        [UIView animateWithDuration:[self transitionDuration:transitionContext] delay:0 options:UIViewAnimationOptionCurveLinear animations:^{
            transView.frame = CGRectMake(0.SCREEN_H.SCREEN_W.SCREEN_H);
            viewCover.alpha= 0;
        } completion:^(BOOL finished) {
            if (!transitionContext.transitionWasCancelled) {
                [viewCover removeFromSuperview];
            }
            
            [transitionContext completeTransition:!transitionContext.transitionWasCancelled];
        }];
    }
}
Copy the code

Handling of gesture conflicts

  1. The way you want to trigger the slide dismiss gesture when you slide the top of the popup ViewController that contains the UITableView.
#pragma mark --UIGestureRecognizerDelegate
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    return YES;
}


- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    // Support multiple gestures
    return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRequireFailureOfGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer NS_AVAILABLE_IOS(7 _0)
{
    // This method returns YES. If the first gesture and the second are mutually exclusive, the first gesture will be invalidated
    if ([otherGestureRecognizer.view isKindOfClass:[UITableView class]]) {
        return YES;
    }
    return NO;
}

Copy the code
  1. Logic when dealing with UITableView sliding to the top
#pragma mark --UIScrollViewDelegate
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    [self setGestureRecognizerEnable:YES scrollView:scrollView];
}

-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    [self setGestureRecognizerEnable:YES scrollView:scrollView];
}

-(void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    // Gestures are available at the end
    [self setGestureRecognizerEnable:YES scrollView:scrollView];
    
}

-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    // Set gesture invalidation when sliding to the top
    if (scrollView.contentOffset.y <=0) {[self setGestureRecognizerEnable:NO scrollView:scrollView];
    }
    
    else
    {
        // Gestures are available
        [self setGestureRecognizerEnable:YES scrollView:scrollView];
    }
}

-(void)setGestureRecognizerEnable:(BOOL)isEnable scrollView:(UIScrollView *)scrollView
{
    for (UIGestureRecognizer *gesRec in  scrollView.gestureRecognizers) {
        if ([gesRec isKindOfClass:[UIPanGestureRecognizer class]]) { gesRec.enabled =isEnable; }}}Copy the code

The way to do that is when you swipe to the top, disable the UITableview gesture, and then trigger the gesture in the current ViewController to slide the gesture on a page.

The DEMO address