引言

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

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

iOS右滑返回的实现及iOS13.4的适配方案_前端

I 、添加右滑返回手势

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

@implementation NavigationController

- (void)viewDidLoad
{
[super viewDidLoad];
//设置右滑返回手势的代理为自身
__weak typeof(self) weakself = self;
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.interactivePopGestureRecognizer.delegate = (id)weakself;
}
}

#pragma
//这个方法是在手势将要激活前调用:返回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设置右滑返回手势的代理为自身
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​​ 进行判定是否返回。

iOS右滑返回的实现及iOS13.4的适配方案_ico_02

- (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

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;



}


III、自定义导航条的rightBarButtonItem

自定义导航条的rightBarButtonItem,采用​​initWithCustomView:rightBtn​​​设置​​rightBtn.frame​​,让文字更大,更容易点击

iOS右滑返回的实现及iOS13.4的适配方案_前端_03

-(void) setuprightBtn{


UIButton *rightBtn = [UIButton buttonWithType:UIButtonTypeCustom];
rightBtn.frame = CGRectMake(0, 0, 44, 44);
// [rightBtn setImage:[UIImage imageNamed:@"icon_shoukuan_shaixuan_n"] forState:UIControlStateNormal];
[rightBtn setTitle:@"编辑" forState:UIControlStateNormal];

[rightBtn setTitleColor:kcellColor forState:UIControlStateNormal];

[rightBtn addTarget:self action:@selector(gotoEditVC) forControlEvents:UIControlEventTouchUpInside];
[rightBtn setImageEdgeInsets:UIEdgeInsetsMake(0, 22, 0, 0)];

UIBarButtonItem *rightButtonItem = [[UIBarButtonItem alloc]initWithCustomView:rightBtn];


self.navigationItem.rightBarButtonItem = rightButtonItem;

self.navigationItem.rightBarButtonItem.customView.hidden = YES;



}

see also

感谢支持,更多内容,请关注公众号:iOS逆向

iOS运行时API应用:

1、实现路由(接口控制app跳任意界面 )

2、获取修改对象的成员属性

3、动态添加/交换方法的实现

4、属性关联​