UINavigationController 全局右劃導(dǎo)致界面假死問題解決

OS原生是支持邊緣滑動返回的,
但如QQ這種全屏全局右劃返回顯然用戶體驗更好,
實現(xiàn)起來其實也不麻煩, 但添加這個功能后可能會導(dǎo)致界面卡死問題,
網(wǎng)上的說法有很多而且不全, 其實導(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.點擊navigationItem返回同時觸發(fā)了手勢.

一般在自定義了返回按鈕出現(xiàn), 例如:自定義button的手勢為UIControlEventTouchDown, 全局右劃返回手勢作用view為self.interactivePopGestureRecognizer.view, 兩者可能會同時觸發(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的時候禁用手勢
    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的時候禁用手勢
    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的時候禁用手勢
    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.interactivePopGestureRecognizer.enabled = NO;
    }
    
    return [super popViewControllerAnimated:animated];
}


- (NSArray<UIViewController *> *)popToRootViewControllerAnimated:(BOOL)animated{

    // pop的時候禁用手勢
    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
        self.interactivePopGestureRecognizer.enabled = NO;
    }
    
    return [super popToRootViewControllerAnimated:animated];
}


- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{

    // push完成后的時候判斷是否在根控制器啟用手勢
    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];
}

// 什么時候調(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;
}

喜歡的話給個喜歡或者關(guān)注一下 3Q~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容