The most comprehensive implementation of iOS full screen return navigation controller

Keywords: github

github download link
The system gesture is added to the view of the navigation controller to realize full screen return. Many people will have many special needs in the visual project,
1: The model comes out with a navigation controller. I think when the sideslip reaches the root controller, the sideslip will be dissmiss
2: If the controller has a scroll view with a long horizontal direction, and the sideslip gesture fails, I need to deal with this
3: Sideslip gestures in a single controller can be disabled
3: The return button is written in the navigation controller, and I want to get the method to transfer when I click the return button

#import <UIKit/UIKit.h>

@interface QDNavigationController : UINavigationController

//Root controller left edge right sliding dissmiss default yes
@property (nonatomic, assign) BOOL needDissMiss;

/* The sub controller writes the following method to turn off full screen return
 reture NO Turn off the full screen return effect of the current page turn on full screen return by default
 - (BOOL)QDNavigationControllerEnabled{
    return NO;
 }
 
 pop Or when dismiss is, call return NO to cancel destroying the page. If you don't cancel destroying the page, return YES; 
 - (BOOL)QDNavigationControllerWillGoBack{
    return YES;
 }
 */
@end



//Screen width
#define kScreenWidth [UIScreen mainScreen].bounds.size.width
//Screen height
#define kScreenHeight [UIScreen mainScreen].bounds.size.height

#import "QDNavigationController.h"

@interface QDNavigationController ()<UIGestureRecognizerDelegate>
//Drag gesture
@property (weak, nonatomic) UIPanGestureRecognizer *pan;

@end

@implementation QDNavigationController

- (void)viewDidLoad{
    [super viewDidLoad];
    
    //navigationBar style
    self.navigationBar.translucent = NO;
    self.navigationBar.barTintColor = [UIColor whiteColor];
    self.navigationBar.backgroundColor = [UIColor whiteColor];
    //   [self.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor blackColor]}];
    
    //Add the system sideslip gesture to the navigation controller view
    NSArray *targets = [self.interactivePopGestureRecognizer valueForKey:@"_targets"];
    id  targetObjc = targets[0];
    id target = [targetObjc valueForKey:@"target"];
    NSString *actionStr = @"handleNavigationTransition:";
    
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:NSSelectorFromString(actionStr)];
    pan.delegate = self;
    self.pan = pan;
    [self.view addGestureRecognizer:pan];
    //Remove the original edge slide gesture 
    self.interactivePopGestureRecognizer.enabled = NO;
    self.needDissMiss = YES;
}

//Is the gesture effective
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
    CGPoint point = [gestureRecognizer locationInView:self.view];
    //pan gesture or not
    BOOL isPanGesture = ([self.pan isEqual:gestureRecognizer]);
    if ([self.pan translationInView:[self.viewControllers lastObject].view].x < 0 || !isPanGesture) {
        return NO;
    }
    
    UIViewController *viewController = [self.viewControllers lastObject];
    //If the sideslip gesture method return NO. is implemented in the sub controller, the gesture does not take effect. - wnundeclared selector ignores the warning
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
    if (isPanGesture && [viewController respondsToSelector:@selector(QDNavigationControllerEnabled)]) {
        if ([viewController performSelector:@selector(QDNavigationControllerEnabled)] == NO) {
            return NO;
        }
    }
#pragma clang diagnostic pop
    
    //Special processing of qualified ScrollView
    BOOL hasScroll = NO;
    //Can I return to full screen
    BOOL needPop = NO;
    for (UIView *subVi in [self.viewControllers lastObject].view.subviews) {
        //This scrollview contains this point. When the content size width is larger than the screen width, the system gesture is blocked by manual pop
        if ([subVi isKindOfClass:NSClassFromString(@"UIScrollView")]
            && ((UIScrollView *)subVi).contentSize.width > kScreenWidth
            ){
            hasScroll = YES;
            //Turn ContentSize of scrollView to frame to determine whether the touchpoint is on scrollView
            CGSize scrollContentSize = ((UIScrollView *)subVi).contentSize;
            CGRect contentSizeRect = CGRectMake(0, 0, scrollContentSize.width, scrollContentSize.height);
            //scrollView can return to full screen on the leftmost touch point of scrollView
            if ( ((UIScrollView *)subVi).contentOffset.x == 0
                && CGRectContainsPoint( contentSizeRect, [self.view convertPoint:point toView:subVi])
                ){
                [((UIScrollView *)subVi).panGestureRecognizer requireGestureRecognizerToFail:self.pan];
                needPop = YES;
                break;
            }
            //Touch point not on scrollView can return to full screen
            if (hasScroll == YES && !CGRectContainsPoint( contentSizeRect, [self.view convertPoint:point toView:subVi])) {
                needPop = YES;
                break;
            }
        }
    }
    
    if (self.viewControllers.count > 1 && (hasScroll == NO || needPop == YES)) {
        if ([self needGoback] == NO) {
            return  NO;
        }else{
            return YES;
        }
    }else{
        if (self.viewControllers.count == 1
            && (hasScroll == NO || needPop == YES)
            && [self.pan translationInView:[self.viewControllers lastObject].view].x > 2) {
            //Root controller meets the condition dismiss
             [self popOrDissMiss];
        }
        return NO;
    }
}

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
    //Sub controller create return button
    if (self.viewControllers.count >= 1 ) {
        viewController.hidesBottomBarWhenPushed = YES;
        viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:[self creatBackButton]];
    }
    [super pushViewController:viewController animated:YES];
}

//Return method
- (void)popOrDissMiss{
    if ([self needGoback] == NO) {
        return ;
    }
    if (self.viewControllers.count == 1 && self.needDissMiss == YES) {
        [self.presentingViewController dismissViewControllerAnimated:YES completion:nil];
        return;
    }
    [self popViewControllerAnimated:YES];
}

//Call the willGoBack method if the self controller complies with the protocol
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
- (BOOL)needGoback{
    UIViewController *viewController = [self.viewControllers lastObject];
    //Implement QDNavigationControllerWillGoBack method in the controller and return NO to cancel destroying the page
    if ([viewController respondsToSelector:@selector(QDNavigationControllerWillGoBack)]) {
        if ([viewController performSelector:@selector(QDNavigationControllerWillGoBack)] == NO) {
            return NO;
        }
    }
    return YES;
}
#pragma clang diagnostic pop


//Back button
- (UIButton *)creatBackButton{
    UIButton *backButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 24.0f, 44.0f)];
    [backButton setImage:[UIImage imageNamed:@"daylight-btn-left-arrow-normal"] forState:UIControlStateNormal];
    [backButton setImage:[UIImage imageNamed:@"daylight-btn-left-arrow-highlighted"] forState:UIControlStateHighlighted];
    [backButton addTarget:self action:@selector(popOrDissMiss) forControlEvents:UIControlEventTouchUpInside];
    return backButton;
}

@end


Posted by tigomark on Thu, 30 Apr 2020 20:57:52 -0700