偷梁換柱---iOS中實(shí)現(xiàn)導(dǎo)航控制器下的所有子控制器全屏滑動(dòng)返回

作為最基本的知識(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)航控制器中)


screenshot.png

導(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)蛋疼了.......

02.gif

注意:滑動(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];
    
}

效果圖:


03.gif

解決:
既然是代理控制的手勢(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;
}

03.gif

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

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

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

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