iOS系統(tǒng)右滑返回全局控制方案
前言
今天有個(gè)小需求,在點(diǎn)擊導(dǎo)航條上的返回按鈕之前要調(diào)用某個(gè)API,并彈出UIAlertView來(lái)顯示,根據(jù)用戶的選項(xiàng)判斷是否是返回還是繼續(xù)留在當(dāng)前控制器。舉個(gè)簡(jiǎn)單的例子,當(dāng)點(diǎn)擊導(dǎo)航條上的左上角返回按鈕時(shí),就調(diào)用我們的API來(lái)提示是否知道,點(diǎn)擊知道則返回,點(diǎn)擊不知道則繼續(xù)留在當(dāng)前控制器。
那么問(wèn)題來(lái)了,導(dǎo)航自帶的右滑返回手勢(shì)在點(diǎn)擊系統(tǒng)的返回按鈕時(shí),不會(huì)沒(méi)有辦法處理,那是自動(dòng)的,因此就要想辦法改成leftBarButtonItem了,但是使用了leftBarButtonItem就沒(méi)有了右滑返回手勢(shì)。
魚(yú)和熊掌不可兼得?筆者自有辦法!
筆者嘗試寫(xiě)個(gè)demo來(lái)驗(yàn)證有什么辦法可以解決,嘗試了以下四種:
- 只在當(dāng)前controller遵守UIGestureRecognizerDelegate并設(shè)置代理為self
- 將UIGestureRecognizerDelegate放在公共基類控制器遵守并設(shè)置代理為self,然后子類重寫(xiě)代理方法
- 將UIGestureRecognizerDelegate放在公共導(dǎo)航類HYBNavigationController里遵守,并設(shè)置代理為導(dǎo)航類,然后重寫(xiě)push/pop相關(guān)的所有方法
- 將UIGestureRecognizerDelegate放在公共導(dǎo)航類HYBNavigationController里遵守,并設(shè)置代理為導(dǎo)航類,但是,只遵守-gestureRecognizerShouldBegin:代理方法
方案一(不可行)
方案一:只在當(dāng)前controller遵守UIGestureRecognizerDelegate并設(shè)置代理為self
為什么不可行呢?當(dāng)想不測(cè)試怎么知道呢?光想是很難考慮全面的。于是寫(xiě)了個(gè)小demo來(lái)測(cè)試。
我們?cè)谠揷ontroller里這樣寫(xiě):
- - (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;
- // 關(guān)鍵行
- self.navigationController.interactivePopGestureRecognizer.delegate = self;
- }
一旦設(shè)置了代理為self,那么使用leftBarButtonItem后就可以實(shí)現(xiàn)點(diǎn)擊回調(diào),而且右滑手勢(shì)還在。
但是,self.navigationController那可是導(dǎo)航控制器對(duì)象的的代理被修改當(dāng)某個(gè)控制器對(duì)象了,當(dāng)這個(gè)控制器類被釋放后,那么代理就為nil了,如此就再也沒(méi)有右滑返回手勢(shì)了。
那么可能有人會(huì)想,在-viewDidAppear:里設(shè)置代理為self,在-viewDidDisappear:時(shí)設(shè)置代理成原來(lái)的代理對(duì)象呢?同樣不可以。當(dāng)A push到B,B push到C,然后從C返回后,代理就不再是最初的導(dǎo)航代理了。
所以,該方案不可行。
方案二(不可行)
方案二:將UIGestureRecognizerDelegate放在公共基類控制器遵守并設(shè)置代理為self,然后子類重寫(xiě)代理方法
筆者嘗試將UIGestureRecognizerDelegate放在HYBBaseViewControlle里遵守,然后實(shí)現(xiàn)代理,默認(rèn)返回YES,表示支持右滑返回。如果要讓某個(gè)控制器不支持右滑返回或者在返回前先執(zhí)行什么操作,可以通過(guò)重寫(xiě)此代理方法來(lái)實(shí)現(xiàn)。
當(dāng)只在一個(gè)控制器里時(shí),這是可以實(shí)現(xiàn)的。但是,當(dāng)這個(gè)控制器被釋放了以后,代理對(duì)象就變成了nil了,因此代理是對(duì)于導(dǎo)航條對(duì)象的,不屬性單個(gè)控制器的。
方案三(可行,但復(fù)雜)
方案三:將UIGestureRecognizerDelegate放在公共導(dǎo)航類HYBNavigationController里遵守,并設(shè)置代理為導(dǎo)航類,然后重寫(xiě)push/pop相關(guān)的所有方法。
如實(shí)現(xiàn)如何下:
- // 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
這是通過(guò)重寫(xiě)所有的pop/push相關(guān)方法,通過(guò)判斷是否要求支持右滑來(lái)設(shè)置。然后,我們要讓某個(gè)控制器類在右滑返回或者點(diǎn)擊返回之前,先調(diào)用我們的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:@"標(biāo)哥的技術(shù)博客"
- 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
這種方案確實(shí)實(shí)現(xiàn)了我們的需求。但是,有沒(méi)有更簡(jiǎn)單的方案呢?今天可能是眼睛有點(diǎn)困的原因,在研究的時(shí)候沒(méi)有意識(shí)到第四種方案。在我準(zhǔn)備寫(xiě)這篇文章的時(shí)候,我再認(rèn)識(shí)地理了一遍邏輯,發(fā)現(xiàn)還有非常簡(jiǎn)單的一種方案可以實(shí)現(xiàn)我的需求。
方案四(可靠,***)
方案四:將UIGestureRecognizerDelegate放在公共導(dǎo)航類HYBNavigationController里遵守,并設(shè)置代理為導(dǎo)航類,但是,只遵守-gestureRecognizerShouldBegin:代理方法。
- @interface HYBNavigationController ()
- @end
- @implementation HYBNavigationController
- - (void)viewDidLoad {
- [super viewDidLoad];
- self.interactivePopGestureRecognizer.delegate = self;
- }
- - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
- BOOL ok = YES; // 默認(rèn)為支持右滑反回
- if ([self.topViewController isKindOfClass:[HYBBaseViewController class]]) {
- if ([self.topViewController respondsToSelector:@selector(gestureRecognizerShouldBegin)]) {
- HYBBaseViewController *vc = (HYBBaseViewController *)self.topViewController;
- ok = [vc gestureRecognizerShouldBegin];
- }
- }
- return ok;
- }
- @end
使用方法與第三種方案一樣,是不是非常地簡(jiǎn)化了?看來(lái)是元宵給我的禮物啊,突然想到這樣的辦法。以前一直沒(méi)有研究過(guò)interactivePopGestureRecognizer屬性,這個(gè)屬性是iOS7以后才有的,因此在項(xiàng)目中一直不能直接使用leftBarButtonItem處理,除非那個(gè)界面不要右滑返回。
現(xiàn)在,一切都明了了,想要使用leftBarButtonItem在公共基類控制器中統(tǒng)一調(diào)用API來(lái)設(shè)置就非常簡(jiǎn)單了,右滑返回手勢(shì)也可以正常使用~
還等什么,趕緊試試吧!
***
如果你所使用的項(xiàng)目也有這樣的需求,不防試試吧!筆者提供了demo的,因此可以先下載demo來(lái)看看效果哦!經(jīng)過(guò)多次測(cè)試,筆者認(rèn)為這是可行的方案,大家若在使用中出現(xiàn)問(wèn)題,還請(qǐng)反饋與筆者,我也想了解是什么情況,當(dāng)然也要找解決方案,共同進(jìn)步嘛。