iOS系統右滑返回全局控制方案

iOS系統右滑返回全局控制方案

鏈接:http://www.lxweimin.com/p/055eb10941e2

前言

今天有個小需求,在點擊導航條上的返回按鈕之前要調用某個API,并彈出UIAlertView來顯示,根據用戶的選項判斷是否是返回還是繼續留在當前控制器。舉個簡單的例子,當點擊導航條上的左上角返回按鈕時,就調用我們的API來提示是否知道,點擊知道則返回,點擊不知道則繼續留在當前控制器。

那么問題來了,導航自帶的右滑返回手勢在點擊系統的返回按鈕時,不會沒有辦法處理,那是自動的,因此就要想辦法改成leftBarButtonItem了,但是使用了leftBarButtonItem就沒有了右滑返回手勢。

魚和熊掌不可兼得?筆者自有辦法!

筆者嘗試寫個demo來驗證有什么辦法可以解決,嘗試了以下四種:

只在當前controller遵守UIGestureRecognizerDelegate并設置代理為self

將UIGestureRecognizerDelegate放在公共基類控制器遵守并設置代理為self,然后子類重寫代理方法

將UIGestureRecognizerDelegate放在公共導航類HYBNavigationController里遵守,并設置代理為導航類,然后重寫push/pop相關的所有方法

將UIGestureRecognizerDelegate放在公共導航類HYBNavigationController里遵守,并設置代理為導航類,但是,只遵守-gestureRecognizerShouldBegin:代理方法

方案一(不可行)

方案一:只在當前controller遵守UIGestureRecognizerDelegate并設置代理為self

為什么不可行呢?當想不測試怎么知道呢?光想是很難考慮全面的。于是寫了個小demo來測試。

我們在該controller里這樣寫:

- (void)viewDidLoad {

?[super viewDidLoad];

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];

[button setTitle:@"返回" forState:UIControlStateNormal];

[button addTarget:self action:@selector(onBack) forControlEvents:UIControlEventTouchUpInside];

[button sizeToFit];

[button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];

UIBarButtonItem *btnItem = [[UIBarButtonItem alloc] initWithCustomView:button];

self.navigationItem.leftBarButtonItem = btnItem;

// 關鍵行

self.navigationController.interactivePopGestureRecognizer.delegate = self;

}

一旦設置了代理為self,那么使用leftBarButtonItem后就可以實現點擊回調,而且右滑手勢還在。

但是,self.navigationController那可是導航控制器對象的的代理被修改當某個控制器對象了,當這個控制器類被釋放后,那么代理就為nil了,如此就再也沒有右滑返回手勢了。

那么可能有人會想,在-viewDidAppear:里設置代理為self,在-viewDidDisappear:時設置代理成原來的代理對象呢?同樣不可以。當A push到B,B push到C,然后從C返回后,代理就不再是最初的導航代理了。

所以,該方案不可行。

方案二(不可行)

方案二:將UIGestureRecognizerDelegate放在公共基類控制器遵守并設置代理為self,然后子類重寫代理方法

筆者嘗試將UIGestureRecognizerDelegate放在HYBBaseViewControlle里遵守,然后實現代理,默認返回YES,表示支持右滑返回。如果要讓某個控制器不支持右滑返回或者在返回前先執行什么操作,可以通過重寫此代理方法來實現。

當只在一個控制器里時,這是可以實現的。但是,當這個控制器被釋放了以后,代理對象就變成了nil了,因此代理是對于導航條對象的,不屬性單個控制器的。

方案三(可行,但復雜)

方案三:將UIGestureRecognizerDelegate放在公共導航類HYBNavigationController里遵守,并設置代理為導航類,然后重寫push/pop相關的所有方法。

如實現如何下:

// ?HYBNavigationController.m

// ?NavRightPanGestureDemo

//

// ?Created by huangyibiao on 16/2/22.

// ?Copyright ? 2016年 huangyibiao. All rights reserved.

//

#import "HYBNavigationController.h"

#import "HYBBaseViewController.h"

@interface HYBNavigationController ()

@property (nonatomic, assign) BOOL enableRightGesture;

@end

@implementation HYBNavigationController

- (void)viewDidLoad {

[super viewDidLoad];

self.enableRightGesture = YES;

self.interactivePopGestureRecognizer.delegate = self;

}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {

return self.enableRightGesture;

}

- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {

if ([viewController isKindOfClass:[HYBBaseViewController class]]) {

if ([viewController respondsToSelector:@selector(gestureRecognizerShouldBegin)]) {

HYBBaseViewController *vc = (HYBBaseViewController *)viewController;

self.enableRightGesture = [vc gestureRecognizerShouldBegin];

}

}

[super pushViewController:viewController animated:YES];

}

- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated {

self.enableRightGesture = YES;

return [super popToRootViewControllerAnimated:animated];

}

- (UIViewController *)popViewControllerAnimated:(BOOL)animated {

if (self.viewControllers.count == 1) {

self.enableRightGesture = YES;

} else {

NSUInteger index = self.viewControllers.count - 2;

UIViewController *destinationController = [self.viewControllers objectAtIndex:index];

if ([destinationController isKindOfClass:[HYBBaseViewController class]]) {

if ([destinationController respondsToSelector:@selector(gestureRecognizerShouldBegin)]) {

HYBBaseViewController *vc = (HYBBaseViewController *)destinationController;

self.enableRightGesture = [vc gestureRecognizerShouldBegin];

}

}

}

return [super popViewControllerAnimated:animated];

}

- (NSArray *)popToViewController:(UIViewController *)viewController animated:(BOOL)animated {

if (self.viewControllers.count == 1) {

self.enableRightGesture = YES;

} else {

UIViewController *destinationController = viewController;

if ([destinationController isKindOfClass:[HYBBaseViewController class]]) {

if ([destinationController respondsToSelector:@selector(gestureRecognizerShouldBegin)]) {

HYBBaseViewController *vc = (HYBBaseViewController *)destinationController;

self.enableRightGesture = [vc gestureRecognizerShouldBegin];

}

}

}

return [super popToViewController:viewController animated:animated];

}

@end

這是通過重寫所有的pop/push相關方法,通過判斷是否要求支持右滑來設置。然后,我們要讓某個控制器類在右滑返回或者點擊返回之前,先調用我們的API判斷,如下:

#import "HYBBController.h"

@implementation HYBBController

- (void)viewDidLoad {

[super viewDidLoad];

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];

[button setTitle:@"返回" forState:UIControlStateNormal];

[button addTarget:self action:@selector(onBack) forControlEvents:UIControlEventTouchUpInside];

[button sizeToFit];

[button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];

UIBarButtonItem *btnItem = [[UIBarButtonItem alloc] initWithCustomView:button];

self.navigationItem.leftBarButtonItem = btnItem;

}

- (BOOL)gestureRecognizerShouldBegin {

[self onBack];

return NO;

}

- (void)onBack {

UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"標哥的技術博客"

message:@"知道博客地址是什么嗎?"

delegate:self

cancelButtonTitle:@"不知道"

otherButtonTitles:@"知道", nil];

[alertView show];

}

#pragma mark - UIAlertViewDelegate

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {

if (buttonIndex == 0) {

} else {

if ([self.navigationItem.title isEqualToString:@"VC6"]) {

NSUInteger index = self.navigationController.viewControllers.count - 3;

UIViewController *vc = [self.navigationController.viewControllers objectAtIndex:index];

[self.navigationController popToViewController:vc animated:YES];

} else {

[self.navigationController popViewControllerAnimated:YES];

}

}

}

@end

這種方案確實實現了我們的需求。但是,有沒有更簡單的方案呢?今天可能是眼睛有點困的原因,在研究的時候沒有意識到第四種方案。在我準備寫這篇文章的時候,我再認識地理了一遍邏輯,發現還有非常簡單的一種方案可以實現我的需求。

方案四(可靠,最優)

方案四:將UIGestureRecognizerDelegate放在公共導航類HYBNavigationController里遵守,并設置代理為導航類,但是,只遵守-gestureRecognizerShouldBegin:代理方法。

@interface HYBNavigationController ()

@end

@implementation HYBNavigationController

- (void)viewDidLoad {

[super viewDidLoad];

self.interactivePopGestureRecognizer.delegate = self;

}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {

BOOL ok = YES; // 默認為支持右滑反回

if ([self.topViewController isKindOfClass:[HYBBaseViewController class]]) {

if ([self.topViewController respondsToSelector:@selector(gestureRecognizerShouldBegin)]) {

HYBBaseViewController *vc = (HYBBaseViewController *)self.topViewController;

ok = [vc gestureRecognizerShouldBegin];

}

}

return ok;

}

@end

使用方法與第三種方案一樣,是不是非常地簡化了?看來是元宵給我的禮物啊,突然想到這樣的辦法。以前一直沒有研究過interactivePopGestureRecognizer屬性,這個屬性是iOS7以后才有的,因此在項目中一直不能直接使用leftBarButtonItem處理,除非那個界面不要右滑返回。

現在,一切都明了了,想要使用leftBarButtonItem在公共基類控制器中統一調用API來設置就非常簡單了,右滑返回手勢也可以正常使用~

還等什么,趕緊試試吧!

最后

如果你所使用的項目也有這樣的需求,不防試試吧!筆者提供了demo的,因此可以先下載demo來看看效果哦!經過多次測試,筆者認為這是可行的方案,大家若在使用中出現問題,還請反饋與筆者,我也想了解是什么情況,當然也要找解決方案,共同進步嘛。

源代碼

請大家到GITHUB下載吧:https://github.com/CoderJackyHuang/NavGestureDemo


?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1,Search Bar 怎樣去掉背景的顏色(storyboard里只能設置background顏色,可是發現cl...
    以德扶人閱讀 2,485評論 2 50
  • iOS7之后蘋果導航控制器增加了一個左側邊緣右滑pop的手勢,在非棧底的控制器里面,從左邊右滑之后相當于出棧(po...
    軟件iOS開發閱讀 485評論 0 0
  • 知進退而后勇,知道自己做錯了選擇,應該及時止損,換個方向或換個角度重新選擇、重新開始,多與別人溝通,取長補短,不要...
    開心的高毛毛閱讀 283評論 0 1
  • 在南方已經好久沒看到積在地面的雪了,今年的雪如此大,還是張素美記憶里的第一次。昨天還是晴天,但清晨起來,滿天飄著星...
    江南的故事酒館閱讀 524評論 0 1
  • 有句話說,性格合適的適合一起玩鬧,性格互補的適合一起變老!哪有什么適不適合的理論,無非就是一方懂得包容理解,一方懂...
    knightmonkey閱讀 210評論 0 1