iOS拦截系统backBarButtonItem 返回事件

系统返回方法 默认是pop回上一个界面,实际情况中并不总是要返回上一个界面,比如用户注册登录之后不想再返回到原来注册登录界面,比如webview里面不停的forword,那么这时候系统返回按钮直接就退出了webview界面,当然我们可以通过设置leftBarButttonItem或者presentViewController的方式,
这里只是一个既能保持系统原有方法的前提下,最小化改动,最大化依赖系统控件

思路是通过runtime method swizzling 拦截点击系统返回事件 ,通过判断UIGestureRecognizerDelegate的手势拦截右滑返回事件

//Returns a Boolean value indicating whether the navigation bar should pop an item.
- (BOOL)navigationBar:(UINavigationBar *)navigationBar 
        shouldPopItem:(UINavigationItem *)item;
复制代码

代码如下
1.创建UINavigationController分类.h

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface UINavigationController (navigationPopBack)

@end

NS_ASSUME_NONNULL_END
复制代码
#import "UIViewController+BackButtonEvent.h"

#import "NSObject+theSwizzling.h"
#import "UINavigationController+navigationPopBack.h"
#import <objc/runtime.h>
static void * const interactivePopGestureDelegate = "interactivePopGestureDelegate";

@implementation UINavigationController (navigationPopBack)
+(void)load{
    //页面进入就加载 区分父类 子类 分类 不覆盖其他类load的实现 initialize 懒加载不一定会实现
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [self methodSwizzlingWithOriginalSelector:@selector(navigationBar:shouldPopItem:) bySwizzlingedSelector:@selector(lsh_navigationBar:shouldPopItem:)];        
    });
}
-(void)viewDidLoad
{
    [super viewDidLoad];
        objc_setAssociatedObject(self,interactivePopGestureDelegate , self.interactivePopGestureRecognizer.delegate, OBJC_ASSOCIATION_ASSIGN);
        self.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
}

- (BOOL)lsh_navigationBar:(UINavigationBar *)navigationBar
        shouldPopItem:(UINavigationItem *)item{
    UIViewController *vc =self.topViewController;
    if (item != vc.navigationItem) {
        return YES;
    }
 
    if ([vc conformsToProtocol:@protocol(BackButtonHandler)]&&[vc respondsToSelector:@selector(test_navigationShouldPopOnBackButton)]) {
        if ([vc test_navigationShouldPopOnBackButton]) {
            return [self lsh_navigationBar:navigationBar shouldPopItem:item];
        }
        else{
            for (UIView *subview in [navigationBar subviews]) {
                if (subview.alpha >0 && subview.alpha<1.0) {
                    [UIView animateWithDuration:.25 animations:^{
                        subview.alpha = 1;
                    }];
                }
            }
            return NO;
        }
    }
    else{
        return [self lsh_navigationBar:navigationBar shouldPopItem:item];
    }
    return NO;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
    if (gestureRecognizer == self.interactivePopGestureRecognizer) {
            
            // 解决根视图侧滑导致push卡死的问题
            if (self.viewControllers.count == 1) {
                return NO;
            }
            
            // 当前视图不是根视图,执行popBack操作
            UIViewController *vc = self.topViewController;
            if([vc respondsToSelector:@selector(test_navigationShouldPopOnBackButton)]) {
                
                return [vc test_navigationShouldPopOnBackButton];
            }
            
            id<UIGestureRecognizerDelegate> originDelegate = objc_getAssociatedObject(self, interactivePopGestureDelegate);
            return [originDelegate gestureRecognizerShouldBegin:gestureRecognizer];
        }
        
        return YES;
}

@end
复制代码

UIViewController+BackButtonEvent 这个分类我之前没这个写过,代理直接在.h文件后实现,自己设置代理,自己引用代理?一般的写法是A文件设置代理,B文件调用A,B作为A的代理实现A的代理方法。而这里A文件设置代理,并作为自己的代理,只是没实现方法而已。

@protocol BackButtonHandler <NSObject>

-(BOOL)test_navigationShouldPopOnBackButton;

@end
@interface UIViewController (BackButtonEvent)<BackButtonHandler>

@end
复制代码
#import "UIViewController+BackButtonEvent.h"

@implementation UIViewController (BackButtonEvent)

@end
复制代码

NSObject+theSwizzling

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSObject (theSwizzling)
+(void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector bySwizzlingedSelector:(SEL)swizzledSelector;
@end
复制代码
#import "NSObject+theSwizzling.h"
#import <objc/message.h>

@implementation NSObject (theSwizzling)
+(void)methodSwizzlingWithOriginalSelector:(SEL)originalSelector bySwizzlingedSelector:(SEL)swizzledSelector{
    Class class = [self class];
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    BOOL didAddMethod = class_addMethod(class, swizzledSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    if (didAddMethod) {
        class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
    }
    else{
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

@end

复制代码

使用方法

#import "UIViewController+BackButtonEvent.h"
#import "WebLSHViewController.h"
@interface WebLSHViewController ()

@end

@implementation WebLSHViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
}
-(BOOL)test_navigationShouldPopOnBackButton
{
    NSLog(@"test_navigationShouldPopOnBackButton");
}
@end
复制代码

本文参考iOS-拦截导航栏backBarButtonItem事件

iOS右滑返回手势深度全解和最佳实施方案

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享