OS原生是支持邊緣滑動返回的,
但如QQ這種全屏全局右劃返回顯然用戶體驗(yàn)更好,
實(shí)現(xiàn)起來其實(shí)也不麻煩, 但添加這個(gè)功能后可能會導(dǎo)致界面卡死問題,
網(wǎng)上的說法有很多而且不全, 其實(shí)導(dǎo)致這種問題會有很多種情況, 這里總結(jié)一下:
卡死的原因有三種:
1.在push或pop的過程中, 接收到新的滑動返回手勢.
主要是界面切換快速操作可能出現(xiàn)
解決辦法:
自定義UINavigationController,
在pushViewController/setViewControllers/popViewControllerAnimated/popToRootViewControllerAnimated中禁用手勢
在代理方法- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated;中啟用手勢
2.在根控制器右劃返回
在上面的代碼中增加判斷, 若子控制器數(shù)量為1, 禁用手勢
3.點(diǎn)擊navigationItem返回同時(shí)觸發(fā)了手勢.
一般在自定義了返回按鈕出現(xiàn), 例如:自定義button的手勢為UIControlEventTouchDown, 全局右劃返回手勢作用view為self.interactivePopGestureRecognizer.view, 兩者可能會同時(shí)觸發(fā)
根據(jù)原因, 改變手勢作用域
UIView *targetView = [[self.viewControllers lastObject] view];
或改變按鈕事件觸發(fā)條件
UIControlEventTouchUpInside
完整添加全局右滑返回且無BUG代碼如下:
@interface LTNavigationController () <UIGestureRecognizerDelegate, UINavigationControllerDelegate>
/**
是否允許右滑返回
*/
@property (nonatomic, assign, getter=isBackGestureEnable) BOOL backGestureEnable;
@end
@implementation LTNavigationController
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationBar.backgroundColor = [UIColor whiteColor];
//設(shè)置全局右滑返回
[self setupRightPanReturn];
[self.navigationItem setHidesBackButton:YES];
self.delegate = self;
}
#pragma mark ---每次push之后生成返回按鈕----
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
if (self.viewControllers.count > 0) {
viewController.navigationItem.leftBarButtonItem = [UIBarButtonItem BarButtonItemWithImg:@"back_black" highlightedImg:nil target:self action:@selector(popViewController)];
viewController.hidesBottomBarWhenPushed = YES;
viewController.edgesForExtendedLayout = UIRectEdgeNone;
viewController.automaticallyAdjustsScrollViewInsets = NO;
}
// push的時(shí)候禁用手勢
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.interactivePopGestureRecognizer.enabled = NO;
self.backGestureEnable = NO;
}
[super pushViewController:viewController animated:animated];
}
- (void)setViewControllers:(NSArray<UIViewController *> *)viewControllers animated:(BOOL)animated{
for (UIViewController *viewController in viewControllers) {
if (viewController != [viewControllers firstObject]) {
viewController.navigationItem.leftBarButtonItem = [UIBarButtonItem BarButtonItemWithImg:@"back_black" highlightedImg:nil target:self action:@selector(popViewController)];
viewController.hidesBottomBarWhenPushed = YES;
viewController.edgesForExtendedLayout = UIRectEdgeNone;
viewController.automaticallyAdjustsScrollViewInsets = NO;
}
}
// push的時(shí)候禁用手勢
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.interactivePopGestureRecognizer.enabled = NO;
self.backGestureEnable = NO;
}
[super setViewControllers:viewControllers animated:animated];
}
- (void)popViewController{
[self popViewControllerAnimated:YES];
}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated{
// pop的時(shí)候禁用手勢
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.interactivePopGestureRecognizer.enabled = NO;
}
return [super popViewControllerAnimated:animated];
}
- (NSArray<UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated{
// pop的時(shí)候禁用手勢
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
self.interactivePopGestureRecognizer.enabled = NO;
}
return [super popToRootViewControllerAnimated:animated];
}
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
// push完成后的時(shí)候判斷是否在根控制器啟用手勢
if ([navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
if ([navigationController.viewControllers count] == 1) {
navigationController.interactivePopGestureRecognizer.enabled = NO;
} else {
self.backGestureEnable = YES;
navigationController.interactivePopGestureRecognizer.enabled = YES;
}
}
}
#pragma mark ---處理全局右滑返回---
- (void)setupRightPanReturn{
// 獲取系統(tǒng)自帶滑動手勢的target對象
id target = self.interactivePopGestureRecognizer.delegate;
// 獲取返回方法
SEL handler = NSSelectorFromString(@"handleNavigationTransition:");
// 獲取添加系統(tǒng)邊緣觸發(fā)手勢的View
UIView *targetView = self.interactivePopGestureRecognizer.view;
// 創(chuàng)建pan手勢 作用范圍是全屏
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:handler];
pan.delegate = self;
[targetView addGestureRecognizer:pan];
// 關(guān)閉邊緣觸發(fā)手勢 防止和原有邊緣手勢沖突
[self.interactivePopGestureRecognizer setEnabled:NO];
}
// 什么時(shí)候調(diào)用:每次觸發(fā)手勢之前都會詢問下代理,是否觸發(fā)。
// 作用:攔截手勢觸發(fā)
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer{
//解決與左滑手勢沖突
CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view];
if (translation.x <= 0 || !self.isBackGestureEnable) {
return NO;
}
return self.childViewControllers.count == 1 ? NO : YES;
}