iOS右滑返回的实现(interactivePopGestureRecognizer)

简介: iOS右滑返回的实现(interactivePopGestureRecognizer)

引言

原理:利用系统的返回手势interactivePopGestureRecognizer进行实现

使用场景:返回按钮有点小,不好触发返回时,可借助右滑返回来提升用户体验

image.png

I 、添加右滑返回手势

若项目有全局的UINavigationController基类,给页面添加右滑返回手势

@implementation NavigationController
- (void)viewDidLoad
{
    [super viewDidLoad];
    //设置右滑返回手势的代理为自身
    __weak typeof(self) weakself = self;
    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.interactivePopGestureRecognizer.delegate = (id)weakself;
    }
}
#pragma mark - UIGestureRecognizerDelegate
//这个方法是在手势将要激活前调用:返回YES允许右滑手势的激活,返回NO不允许右滑手势的激活
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if (gestureRecognizer == self.interactivePopGestureRecognizer) {
        //屏蔽调用rootViewController的滑动返回手势,避免右滑返回手势引起死机问题
        if (self.viewControllers.count < 2 ||
 self.visibleViewController == [self.viewControllers objectAtIndex:0]) {
            return NO;
        }
    }
    //这里就是非右滑手势调用的方法啦,统一允许激活
    return YES;
}

II、QMUI导致右滑返回没有生效的解决方法

先来看看QMUI如何实现实现右滑返回?

2.1 UINavigationController (QMUI)进行控制右滑返回

QMUI使用分类UINavigationController (QMUI)方式进行控制右滑返回,具体核心代码如下

  1. 重写viewDidLoad设置右滑返回手势的代理为自身
  ExtendImplementationOfVoidMethodWithoutArguments([UINavigationController class], @selector(viewDidLoad), ^(UINavigationController *selfObject) {
            selfObject.qmui_interactivePopGestureRecognizerDelegate = selfObject.interactivePopGestureRecognizer.delegate;
            selfObject.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)selfObject;
        });
  1. gestureRecognizerShouldBegin

这个方法是在手势将要激活前调用:返回YES允许右滑手势的激活,返回NO不允许右滑手势的激活

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
    if (gestureRecognizer == self.interactivePopGestureRecognizer) {
        BOOL canPopViewController = [self canPopViewController:self.topViewController byPopGesture:YES];
        if (canPopViewController) {
            if ([self.qmui_interactivePopGestureRecognizerDelegate respondsToSelector:_cmd]) {
                return [self.qmui_interactivePopGestureRecognizerDelegate gestureRecognizerShouldBegin:gestureRecognizer];
            } else {
                return NO;
            }
        } else {
            return NO;
        }
    }
    return YES;
}
  1. iOS 13.4 开始会优先询问shouldReceiveEvent方法,只有返回 YES 后才会继续后续的逻辑
- (BOOL)_gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveEvent:(UIEvent *)event {
    if (gestureRecognizer == self.interactivePopGestureRecognizer) {
        NSObject <UIGestureRecognizerDelegate> *originGestureDelegate = self.qmui_interactivePopGestureRecognizerDelegate;
        if ([originGestureDelegate respondsToSelector:_cmd]) {
            BOOL originalValue = YES;
            [originGestureDelegate qmui_performSelector:_cmd withPrimitiveReturnValue:&originalValue arguments:&gestureRecognizer, &event, nil];
            if (!originalValue && [self shouldForceEnableInteractivePopGestureRecognizer]) {
                return YES;
            }
            return originalValue;
        }
    }
    return YES;
}

其中在第三步中shouldForceEnableInteractivePopGestureRecognizer调用了UINavigationControllerBackButtonHandlerProtocol协议的forceEnableInteractivePopGestureRecognizer进行判定是否返回。

image.png

- (BOOL)shouldForceEnableInteractivePopGestureRecognizer {
    UIViewController *viewController = [self topViewController];
    return self.viewControllers.count > 1 && self.interactivePopGestureRecognizer.enabled && [viewController respondsToSelector:@selector(forceEnableInteractivePopGestureRecognizer)] && [viewController forceEnableInteractivePopGestureRecognizer];
}


当自定义了leftBarButtonItem按钮之后,系统的手势返回就失效了。

可以通过forceEnableInteractivePopGestureRecognizer来决定要不要把那个手势返回强制加回来。当 interactivePopGestureRecognizer.enabled = NO 或者当前UINavigationController堆栈的viewControllers小于2的时候此方法无效。

自定义了leftBarButtonItem按钮

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
    viewController.hidesBottomBarWhenPushed = self.viewControllers.count == 1;
    if (self.viewControllers.count>0) {
        [self setNavigationBarHidden:NO animated:NO];
//        viewController.hidesBottomBarWhenPushed =YES;
        //设置左边按钮
        UIBarButtonItem *backItem      =nil;
        if ([viewController respondsToSelector:@selector(KNbackAction)]) {
             backItem =[[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:QCTNAVicon_left] style:0 target:viewController action:@selector(KNbackAction)];
        }else{
            backItem =[[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:QCTNAVicon_left] style:0 target:self action:@selector(backAction)];
        }
            viewController.navigationItem.leftBarButtonItems = @[backItem];
    }
    [super pushViewController:viewController animated:animated];
}

2.2 解决方法

所以当你自定义导航栏(自定义了leftBarButtonItem按钮)没采用系统的默认的实现,发生当前不可以手势返回,可先检查为什么当前状态,系统不允许你的手势返回,例如是否隐藏了 navigationBar,或者隐藏了系统的返回按钮?

比如push的时候,自定义了leftBarButtonItem按钮了,你可以采用分类方式往UIViewController 添加forceEnableInteractivePopGestureRecognizer方法将手势返回强制加回来

2.3 动态添加方法

使用场景:

  1. 在消息发送和消息转发时会用到动态添加方法
  2. 全局控制返回手势

下面的+addMethod方法有三个参数,第一个参数是要添加方法的类,第二个参数是方法的SEL,第三个参数则是提供方法实现的SEL。

使用class_getInstanceMethod()method_getImplementation()获取相应SEL。

下方的IMP其实就是Implementation的方法缩写,获取到相应的方法实现后,然后再调用class_addMethod()方法将IMP与SEL进行绑定即可。

/**
 往类上添加新的方法与其实现
 @param class 相应的类
 @param methodSel 添加的方法
 @param methodSelImpl 包含方法实现的SEL
 */
+ (void)addMethod:(Class)class method:(SEL)methodSel method:(SEL)methodSelImpl {
    Method method = class_getInstanceMethod(class, methodSelImpl);
    IMP methodIMP = method_getImplementation(method);
    const char *types = method_getTypeEncoding(method);
    class_addMethod(class, methodSel, methodIMP, types);
}

往UIViewController 添加forceEnableInteractivePopGestureRecognizer方法将手势返回强制加回来

    @implementation UIViewController (ERPPresent13)
+ (void)load {
    [self addMethod:self.class method:@selector(forceEnableInteractivePopGestureRecognizer) method:@selector(kunnan_forceEnableInteractivePopGestureRecognizer)];
}
- (BOOL)kunnan_forceEnableInteractivePopGestureRecognizer {
    return YES;
}

image.png

see also

iOS运行时API应用:1、实现路由(接口控制app跳任意界面 )2、获取修改对象的成员属性3、动态添加/交换方法的实现4、属性关联

目录
相关文章
|
安全 数据安全/隐私保护 iOS开发
iOS小技能:【发红包】使用tweak和lua脚本结合进行实现
我们开发的大部分越狱程序,都是编译成动态链接库(`例如:介绍的越狱程序(Tweak)开发,就是动态链接库。`),然后通过越狱平台的MobileSubstrate(iOS7上叫CydiaSubstrate)来加载进入目标程序(Target),通过对目标程序的挂钩(Hook),来实现相应的功能。
321 0
|
Android开发 iOS开发
iOS开发 - 商品详情页两种分页模式,只提供思路和实现方式。
iOS开发 - 商品详情页两种分页模式,只提供思路和实现方式。
402 0
iOS开发 - 商品详情页两种分页模式,只提供思路和实现方式。
|
存储 安全 iOS开发
iOS开发 - 继udid,Mac地址等一系列唯一标识无效后,如何用KeyChain来实现设备唯一性
iOS开发 - 继udid,Mac地址等一系列唯一标识无效后,如何用KeyChain来实现设备唯一性
458 0
iOS开发 - 继udid,Mac地址等一系列唯一标识无效后,如何用KeyChain来实现设备唯一性
|
Swift 数据安全/隐私保护 iOS开发
iOS开发 - swift通过Alamofire实现https通信
iOS开发 - swift通过Alamofire实现https通信
411 0
iOS开发 - swift通过Alamofire实现https通信
|
开发者 iOS开发
iOS开发 - 用AFNetworking实现https单向验证,双向验证
iOS开发 - 用AFNetworking实现https单向验证,双向验证
399 0
iOS开发 - 用AFNetworking实现https单向验证,双向验证
|
iOS开发
iOS小技能:自动布局实现兄弟控件N等分且宽高比例是1:N(xib 上实现)
本文为 iOS视图约束专题的第三篇:xib上使用自动布局教程
173 0
|
Linux iOS开发 开发者
实现在windows、linux下上传ios app到App Store
实现在windows、linux下上传ios app到App Store
实现在windows、linux下上传ios app到App Store
|
小程序 前端开发 Shell
接入 mPaaS 小程序并实现启动 iOS 版| 学习笔记
快速学习接入 mPaaS 小程序并实现启动 iOS 版。
649 0
接入 mPaaS 小程序并实现启动 iOS 版| 学习笔记
|
小程序 Shell 开发工具
接入 mpaas 小程序并实现启动 IOS 版|学习笔记
快速学习接入 mpaas 小程序并实现启动 IOS 版
228 0
接入 mpaas 小程序并实现启动 IOS 版|学习笔记
|
程序员 API Android开发
iOS开发:简单的Toast提示框实现
博主是以iOS开发出身,那就最后一篇博文就分享一下关于iOS的内容吧。iOS开发过程中,有些时候操作App的时候,需要给用户对应的响应提示操作,使用系统自带的提示框不是每种情况都适用的。
703 0
iOS开发:简单的Toast提示框实现