作為最基本的知識(shí),我們都知道通過(guò)導(dǎo)航控制器push出來(lái)子控制器時(shí),系統(tǒng)會(huì)默認(rèn)給導(dǎo)航條的左上角設(shè)置一個(gè)返回按鈕.樣式是默認(rèn)如下圖的.
(本文所有的代碼設(shè)置都是應(yīng)該寫(xiě)在你當(dāng)前設(shè)置的導(dǎo)航控制器中)

導(dǎo)航控制器還有個(gè)屬性:
self.interactivePopGestureRecognizer
這是系統(tǒng)給導(dǎo)航控制器的view添加的一個(gè)滑動(dòng)返回的手勢(shì),也就是在導(dǎo)航控制器的子控制器下,你除了可以通過(guò)點(diǎn)擊左上角返回按鈕返回上一級(jí),也可以通過(guò)滑動(dòng)返回,到這里感覺(jué)還挺貼心的,不過(guò)要注意用過(guò)這個(gè)功能的同學(xué)們應(yīng)該都知道這個(gè)功能僅限于你滑動(dòng)的是屏幕最左側(cè)的邊緣,要不就是無(wú)效的.這就有點(diǎn)蛋疼了.......

注意:滑動(dòng)區(qū)域小還不只是最蛋疼的問(wèn)題,這個(gè)手勢(shì)不是在任何情況下都有效的.如果我們不用系統(tǒng)默認(rèn)的返回按鈕,而是通過(guò)自定義的方式,那么這個(gè)手勢(shì)就會(huì)失效.
造成這個(gè)手勢(shì)失效的原因:
默認(rèn)情況下,只要用的是系統(tǒng)默認(rèn)的返回按鈕,那么控制手勢(shì)是否被觸發(fā)的那個(gè)代理方法的返回值就是yes,也就是說(shuō)手勢(shì)可以用,但是這里如果覆蓋了導(dǎo)航條上左側(cè)按鈕,那么就會(huì)觸發(fā)代理方法,返回NO,那么手勢(shì)就會(huì)失效.
#pragma mark - 手勢(shì)代理方法
//是否觸發(fā)手勢(shì)
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
//控制是否觸發(fā)手勢(shì),如果用系統(tǒng)默認(rèn)樣式的返回按鈕,這里返回值默認(rèn)是YES
}
這里我是在當(dāng)前導(dǎo)航控制器中通過(guò)攔截push方法,來(lái)統(tǒng)一設(shè)置導(dǎo)航控制器的子控制器的返回按鈕的樣式,所以如果說(shuō)以后項(xiàng)目中發(fā)現(xiàn),所有子控制器的返回按鈕都是一樣的,通過(guò)這種方法統(tǒng)一設(shè)置比較方便
//重寫(xiě)push方法來(lái)統(tǒng)一設(shè)置push出來(lái) 的控制器的導(dǎo)航條上的返回按鈕,根控制器除外
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
// 先判斷是不是根控制器,當(dāng)?shù)谝淮卧O(shè)置導(dǎo)航控制器的根控制器的時(shí)候,會(huì)來(lái)到這個(gè)方法,這時(shí)候?qū)Ш娇刂破鞯淖涌刂破鲾?shù)為0,設(shè)置成功之后就是1 ,之后每次push出新的控制器都會(huì)來(lái)到這個(gè)方法
if (self.childViewControllers.count > 0)//過(guò)濾掉根控制器
{
// 設(shè)置返回按鈕
UIButton *backButton = [[UIButton alloc] init];
[backButton setTitle:@"返回" forState:UIControlStateNormal];
[backButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[backButton setTitleColor:[UIColor redColor] forState:UIControlStateHighlighted];
[backButton addTarget:self action:@selector(back) forControlEvents:UIControlEventTouchUpInside];
// 設(shè)置按鈕尺寸自適應(yīng)
[backButton sizeToFit];
viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
}
// 一定要調(diào)用父類的push的方法
[super pushViewController:viewController animated:animated];
}
效果圖:

解決:
既然是代理控制的手勢(shì),那么可以把這個(gè)手勢(shì)的代理清空,就不會(huì)觸發(fā)代理方法,所以就可以恢復(fù)滑動(dòng)返回.
這時(shí)候會(huì)出現(xiàn)新的問(wèn)題:
就是滑動(dòng)到導(dǎo)航控制器的根控制器的時(shí)候,會(huì)出現(xiàn)應(yīng)用程序假死的狀況.解決這個(gè)問(wèn)題我們可以設(shè)置手勢(shì)代理為當(dāng)前導(dǎo)航控制器
//設(shè)置手勢(shì)代理為當(dāng)前導(dǎo)航控制器
self.interactivePopGestureRecognizer.delegate = self;
在代理方法: // 是否觸發(fā)手勢(shì)
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
// 在根控制器下 不要 觸發(fā)手勢(shì)
return self.childViewControllers.count > 1;
}
這樣就能解決滑動(dòng)到根控制器時(shí)出現(xiàn)程序假死的情況了.
接下來(lái)進(jìn)入重點(diǎn):如何實(shí)現(xiàn)全屏滑動(dòng)返回上一級(jí)
目前很多App都有這種功能,比如瀏覽器類的App.因?yàn)橄到y(tǒng)之前的手勢(shì)滑動(dòng)區(qū)域比較小,所以我們需要把它之前的手勢(shì)替換掉.也就是添加一個(gè)新的手勢(shì) UIPanGestureRecognizer,這是最基本要做的.
實(shí)現(xiàn)這個(gè)功能之前我們先研究下系統(tǒng)是如何實(shí)現(xiàn)默認(rèn)的滑動(dòng)返回上一級(jí)的功能的.其實(shí)就是研究這個(gè)手勢(shì)屬性,通過(guò)查看它的信息,來(lái)看它是如何實(shí)現(xiàn)滑動(dòng)返回的
self.interactivePopGestureRecognizer
我先通過(guò)打印輸出了這個(gè)手勢(shì)的信息:
NSLog(@"系統(tǒng)手勢(shì)信息---%@",self.interactivePopGestureRecognizer);
輸出結(jié)果:
demo[27061:1229271] 系統(tǒng)手勢(shì)信息---<UIScreenEdgePanGestureRecognizer: 0x7fddba56d6c0; state = Possible; delaysTouchesBegan = YES; view = <UILayoutContainerView 0x7fddba79e600>;
target= <(
action=handleNavigationTransition:,
target=<_UINavigationInteractiveTransition 0x7fddba56d130>)>>
我們可以發(fā)現(xiàn)里面幾個(gè)關(guān)鍵的信息:
action=handleNavigationTransition: 這應(yīng)該就是實(shí)現(xiàn)滑動(dòng)返回功能的方法,也就是滑動(dòng)是觸發(fā)的方法.
target=<_UINavigationInteractiveTransition 0x7fddba56d130 這個(gè)對(duì)象是實(shí)現(xiàn)這個(gè)手勢(shì)觸發(fā)方法的對(duì)象.
接下來(lái)我又打印了手勢(shì)的代理
NSLog(@"系統(tǒng)手勢(shì)代理----%@",self.interactivePopGestureRecognizer.delegate);
打印出來(lái)的信息
系統(tǒng)手勢(shì)代理----<_UINavigationInteractiveTransition: 0x7fddba56d130>
重點(diǎn)來(lái)了:
你會(huì)驚奇的發(fā)現(xiàn),手勢(shì)的代理和之前打印出來(lái)的手勢(shì)中的target對(duì)象內(nèi)存地址是一樣.通過(guò)這個(gè)可以斷定系統(tǒng)的做法就是在自己的代理對(duì)象中實(shí)現(xiàn)了監(jiān)聽(tīng)滑動(dòng)返回手勢(shì)的方法的.
這樣問(wèn)題其實(shí)已經(jīng)解決的差不多了.我們完全來(lái)個(gè)偷梁換柱,按照系統(tǒng)的做法去添加我們自己的滑動(dòng)手勢(shì),畢竟總重要的兩個(gè)信息我們已經(jīng)得到了.現(xiàn)在要做的工作就是把系統(tǒng)的手勢(shì)替換掉.
- (void)viewDidLoad {
[super viewDidLoad];
//打印之前的手勢(shì),來(lái)看看系統(tǒng)是怎么設(shè)置這個(gè)手勢(shì)的
NSLog(@"系統(tǒng)手勢(shì)信息---%@",self.interactivePopGestureRecognizer);
NSLog(@"系統(tǒng)手勢(shì)代理----%@",self.interactivePopGestureRecognizer.delegate);
// 添加全屏滑動(dòng)返回手勢(shì)
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self.interactivePopGestureRecognizer.delegate action:@selector(handleNavigationTransition:)];
[self.view addGestureRecognizer:pan];
// 設(shè)置手勢(shì)的代理,主要用來(lái)控制手勢(shì)什么時(shí)候被觸發(fā)
pan.delegate = self;
// 把之前的手勢(shì)禁用掉
self.interactivePopGestureRecognizer.enabled = NO;
}
同樣為了避免滑動(dòng)到導(dǎo)航根控制器的根控制器出現(xiàn)假死的情況:
這里導(dǎo)航控制器要遵守協(xié)議
<UIGestureRecognizerDelegate>
#pragma mark - 手勢(shì)代理方法
//是否觸發(fā)手勢(shì)
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
// 根控制器不需要手勢(shì)
return self.childViewControllers.count > 1;
}

到此為止通過(guò)偷梁換柱的方法,已經(jīng)實(shí)現(xiàn)了導(dǎo)航控制器下子控制器全屏滑動(dòng)返回的功能