iOS | 全屏右滑返回詳解

隨著手機(jī)屏幕的變大,原來(lái)右滑返回略顯不夠人性化,尤其是對(duì)手小的朋友,讓我如何單手玩手機(jī).對(duì)于app要全屏右滑或保持原生邊緣觸發(fā),各有說(shuō)辭,這里不討論其好壞.

下面先看一下實(shí)現(xiàn)效果.


全屏pop

效果還不錯(cuò)吧.當(dāng)然了,這里的所有效果都是系統(tǒng)實(shí)現(xiàn)的.或許你不信,一起看看實(shí)現(xiàn)吧.

#import "GLNavigationController.h"

@interface GLNavigationController () <UIGestureRecognizerDelegate>

@end

@implementation GLNavigationController 

- (void)viewDidLoad {
    [super viewDidLoad];
    //  這句很核心 稍后講解
    id target = self.interactivePopGestureRecognizer.delegate;
    //  這句很核心 稍后講解
    SEL handler = NSSelectorFromString(@"handleNavigationTransition:");
    //  獲取添加系統(tǒng)邊緣觸發(fā)手勢(shì)的View
    UIView *targetView = self.interactivePopGestureRecognizer.view;
    
    //  創(chuàng)建pan手勢(shì) 作用范圍是全屏
    UIPanGestureRecognizer * fullScreenGes = [[UIPanGestureRecognizer alloc]initWithTarget:target action:handler];
    fullScreenGes.delegate = self;
    [targetView addGestureRecognizer:fullScreenGes];
    
    // 關(guān)閉邊緣觸發(fā)手勢(shì) 防止和原有邊緣手勢(shì)沖突
    [self.interactivePopGestureRecognizer setEnabled:NO];
}

//  防止導(dǎo)航控制器只有一個(gè)rootViewcontroller時(shí)觸發(fā)手勢(shì)
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
//解決與左滑手勢(shì)沖突  
  CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view];
    if (translation.x <= 0) {
        return NO;
    }
    // 過(guò)濾執(zhí)行過(guò)渡動(dòng)畫時(shí)的手勢(shì)處理
   if ([[self valueForKey:@"_isTransitioning"] boolValue]) {
        return NO;
   }

    return self.childViewControllers.count == 1 ? NO : YES;
}

@end

在實(shí)現(xiàn)之前,先推測(cè)一下蘋果實(shí)現(xiàn)pop的大概思路.首先,需要在一個(gè)合適的view上添加邊緣手勢(shì),其次,針對(duì)這個(gè)手勢(shì)必然要實(shí)現(xiàn)一個(gè)方法響應(yīng)該事件.當(dāng)然,根據(jù)蘋果一貫代碼風(fēng)格,處理該事件很可能交給另一個(gè)專門的類去處理.

假如以上推測(cè)成立,只要獲得那個(gè)專門處理事件的類和方法,實(shí)現(xiàn)全屏pop效果就很簡(jiǎn)單了.

下面是筆者在分析蘋果實(shí)現(xiàn)pop的部分信息.看到這,是否若有所悟?

(lldb) pclass [self interactivePopGestureRecognizer]
// 信息->1
UIScreenEdgePanGestureRecognizer
   | UIPanGestureRecognizer
   |    | UIGestureRecognizer
   |    |    | NSObject
(lldb) pclass [self interactivePopGestureRecognizer].delegate
// 信息->2
_UINavigationInteractiveTransition
   | _UINavigationInteractiveTransitionBase
   |    | UIPercentDrivenInteractiveTransition
   |    |    | NSObject
(lldb) po [self interactivePopGestureRecognizer]
// 信息->3
<UIScreenEdgePanGestureRecognizer: 0x7fab1243be00; state = Possible; enabled = NO; delaysTouchesBegan = YES; view = <UILayoutContainerView 0x7fab126a4a60>; target= <(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7fab1243b850>)>>

(lldb) po [self interactivePopGestureRecognizer].delegate
// 信息->4
<_UINavigationInteractiveTransition: 0x7fab1243b850>

(lldb) 

從信息1中,可以知道interactivePopGestureRecognizer屬性并不是UIGestureRecognizer類型的對(duì)象,而是其子類UIPanGestureRecognizer的子類UIScreenEdgePanGestureRecognizer類型的對(duì)象.

UIScreenEdgePanGestureRecognizer是邊緣觸發(fā)手勢(shì),在系統(tǒng)中公有API,里面只有一個(gè)edges屬性,用來(lái)設(shè)置具體邊緣有效,如左邊緣.具體可以參考官方API.

在信息3中,可以看到

target= (action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7fab1243b850>)

這樣一條信息,里面包含了target和action.看到這是不是很興奮?iOS開發(fā)者再也屬性不過(guò)的目標(biāo)-動(dòng)作模式了.

到這里,已經(jīng)可以確定蘋果的實(shí)現(xiàn)方式是通過(guò)邊緣觸發(fā)手勢(shì)處理pop的.這里target是私有的,如何獲得呢?于是,網(wǎng)上很多人開始使用runtime來(lái)獲得一些私有的方法.筆者一般不愿在正式上線的項(xiàng)目中使用runtime獲得私有API,雖然不一定會(huì)被蘋果拒接,但是會(huì)有一定風(fēng)險(xiǎn),畢竟筆者最近人品還沒(méi)爆發(fā).

有沒(méi)不用運(yùn)行時(shí)的好方法?

先別著急,繼續(xù)看信息2和4, interactivePopGestureRecognizer的代理是_UINavigationInteractiveTransition,看類名可以想到該類和交互轉(zhuǎn)場(chǎng)相關(guān).分析到這里,基本上可以推測(cè)出蘋果是通過(guò)代理將事件處理委托給了_UINavigationInteractiveTransition對(duì)象.

在信息3中,可以看到target=<_UINavigationInteractiveTransition 0x7fab1243b850>的地址是0x7fab1243b850,信息4中<_UINavigationInteractiveTransition: 0x7fab1243b850>的地址也是0x7fab1243b850.

由以上分析,可以確定蘋果的實(shí)現(xiàn)方式是將處理邊緣觸發(fā)的事件的任務(wù)委托給了_UINavigationInteractiveTransition,在_UINavigationInteractiveTransition中有處理該事件的方法handleNavigationTransition:.

代碼分析

id target = self.interactivePopGestureRecognizer.delegate;

這句代碼目的是獲取事件處理對(duì)象.以便自己添加的手勢(shì)可以把事件處理委托給它.

SEL handler = NSSelectorFromString(@"handleNavigationTransition:");

這句就是獲取委托對(duì)象里的處理方法.

    UIPanGestureRecognizer * fullScreenGes = [[UIPanGestureRecognizer alloc]initWithTarget:target action:handler];
    fullScreenGes.delegate = self;
    [targetView addGestureRecognizer:fullScreenGes];

這幾句就是添加自己的全屏手勢(shì),通過(guò)目標(biāo)-動(dòng)作模式把任務(wù)交給了系統(tǒng)委托對(duì)象處理.

建議

如果需要自定制導(dǎo)航時(shí),實(shí)現(xiàn)是寫在UINavigationController子類中,比較方便.如果不需要,可以單獨(dú)寫一個(gè)分類.這里寫在GLNavigationController中,其中GLNavigationController.h繼承自UINavigationController.

其他提示

  • 如果你的導(dǎo)航在不同控制器間有隱藏狀態(tài)欄的話,隱藏方法需要使用帶有animated:參數(shù)的方法setNavigationBarHidden: animated:,否則過(guò)渡會(huì)出問(wèn)題.
  • 不用擔(dān)心審核問(wèn)題,是可以通過(guò)的.

點(diǎn)擊下載源碼

*溫馨提示:如果對(duì)筆者分析時(shí)使用的工具感興趣,可以在筆者的博客找到相關(guān)文章.

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,024評(píng)論 4 61
  • 前幾天看了@欒小布的一篇文章:Custom backBarButtonItem,在跟著做的時(shí)候我又順便擴(kuò)展了一些,...
    Dashing_Pro閱讀 20,942評(píng)論 26 151
  • 愛是恒久忍耐,又有恩慈;愛是不嫉妒,愛是不自夸,不張狂,不作害羞的事,不求自己的益處,不輕易發(fā)怒,不計(jì)算人的惡,不...
    岳曉晴閱讀 652評(píng)論 0 0
  • 七律/無(wú)刺枸骨 作者:心博、圖片:網(wǎng)絡(luò) 枸骨純?nèi)蛔兎N成,幾經(jīng)脫刺一身輕。 黃花嬌小微微綠,嫩葉稍稀熠熠晶。 入夏芬...
    心博1閱讀 1,390評(píng)論 0 0

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