導航控制器的左滑返回

2016年7月30日

導航控制器的左滑返回

默認狀態(tài)下魂贬,系統(tǒng)提供了導航控制器的左滑返回的功能。系統(tǒng)功能的前提是:

  • 導航控制器back按鈕是系統(tǒng)自帶的---不能自定義leftBarButton
  • 系統(tǒng)只提供滑動邊緣返回,不提供全屏滑動手勢

接下來,我們來闡述如何已經(jīng)自定義leftBarButton的前提下,實現(xiàn)滑動返回捎拯。

下面的方法,其實都是巧妙的借用了系統(tǒng)的返回方法盲厌。

1 左滑邊緣返回

1.1 思考:

當我們自定義了leftBarButton之后署照,系統(tǒng)的左邊緣滑動手勢就不起作用了。我們怎么才能做到吗浩,自定義leftBarButton之后建芙,還讓系統(tǒng)的左邊緣滑動手勢起作用?

1.2 分析:

我們在自定義手勢的時候懂扼,手勢一旦添加禁荸,當觸發(fā)手勢的時候,就會按照action去執(zhí)行響應的代碼阀湿,系統(tǒng)提供的左滑動邊緣返回的手勢也是一樣赶熟。對手勢的監(jiān)聽(是否執(zhí)行當前手勢、執(zhí)行的過程等)都是通過代理來完成的陷嘴,也就是通過代理來控制當前手勢觸發(fā)后映砖,是否執(zhí)行相應代碼。那我們就能得出一個結(jié)論:<b style="color:red">自定義leftBarButton后系統(tǒng)返回手勢失效灾挨,根本原因是系統(tǒng)返回手勢的代理在其中起作用邑退,我們要做的是,禁止將系統(tǒng)返回手勢的代理清空掉劳澄。這樣手勢就一直存在了地技。</b>

1.3 問題

滑動返回到上一個控制器,其根本原理是秒拔,將當前導航控制器的棧頂控制器出棧莫矗,pop掉。那么問題來了砂缩,當返回到根控制器后作谚,如果再次嘗試返回時,就意味著要講根控制器pop掉梯轻,界面就會卡死食磕。正常情況下,是由系統(tǒng)手勢的代理去判斷當前棧頂控制器是否為根控制器喳挑。所以我們的思路是:<b style="color:red">通過清空系統(tǒng)手勢的代理來維持手勢一致存在,確保左滑返回可用。且伊诵,當當前棧頂控制器為非根控制器的時候才清空系統(tǒng)手勢的代理单绑,如果是根控制器,就恢復之前的代理(意味著曹宴,清空之前要保存)</b>搂橙。

1.4 代碼示例

CMNavigationController.m

@interface CMNavigationController () <UINavigationControllerDelegate>

@property(nonatomic,strong) id popGestureDelegate; //用來保存系統(tǒng)手勢的代理

@end

@implementation CMNavigationController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    #warning 我們不能直接將代理置空,需要根據(jù)當前棧頂控制器是否是根控制器進行判斷笛坦。
    //    self.interactivePopGestureRecognizer.delegate = nil;

    #warning 第一步区转,先保存當前的代理
    self.popGestureDelegate = self.interactivePopGestureRecognizer.delegate;
    
    #warning 第二步,成為自己的代理版扩,去監(jiān)聽pop的過程废离,pop之前判斷是否為根控制器
    self.delegate = self;

}

#warning 第三步,監(jiān)聽pop的方法礁芦,判斷當前的棧頂控制器是否為根控制器
-(void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    //self.viewControllers[0]表示根控制器
    if([self.topViewController isEqual:self.viewControllers[0]])
    {
        //如果是根控制器蜻韭,恢復系統(tǒng)手勢默認的代理
        self.interactivePopGestureRecognizer.delegate = self.popGestureDelegate;
    
    }else
    {
        //如果是非根控制器,將系統(tǒng)手勢的默認代理置空
        self.interactivePopGestureRecognizer.delegate = nil;
    }
}

2 左滑全屏(任意位置)返回

2.1 思考

就算我們不自定義leftBarButton柿扣,系統(tǒng)的手勢也沒辦法幫我們實現(xiàn)全屏返回肖方。<b style="color:red">也就是說,手勢必須由我們自己去定義未状。</b>

2.2 分析

自定義手勢的時候俯画,我們只需要定義手勢的執(zhí)行方法action和方法的執(zhí)行對象target即可。由于全屏返回這個手勢功能比較復雜司草,<b style="color:red">所以我們目前的首要任務不是去自己實現(xiàn)action活翩,而是去借用系統(tǒng)的target對象的action方法。</b>因為翻伺,不管是系統(tǒng)的左滑邊緣返回手勢材泄,還是自定義全屏左滑返回手勢,返回這個功能都是一樣的吨岭。

2.3 問題

目前拉宗,我們的任務有兩個:①找到系統(tǒng)的action方法名稱;②找到系統(tǒng)的target對象(因為action方法是target對象的)辣辫。第一個任務很簡單旦事,通過打印系統(tǒng)的手勢就可以知道方法名稱。第二個任務就困難了急灭。有兩種方式:①runtime去分析當前系統(tǒng)的屬性 ②通過自定義手勢的規(guī)律去猜測姐浮。

2.4 找到action方法名稱

第一步,打印系統(tǒng)手勢

//1 打印系統(tǒng)手勢
/**
 *  打印結(jié)果
    <UIScreenEdgePanGestureRecognizer: 0x7ffaea42b2f0; state = Possible; delaysTouchesBegan = YES; view = <UILayoutContainerView 0x7ffaea787b50>; target= <(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7ffaea42ae20>)>>
 */
 
NSLog(@"\n%@ \n",self.interactivePopGestureRecognizer);

<b style="color:red">第二步葬馋,通過打印結(jié)果看卖鲤,執(zhí)行action方法是handleNavigationTransition </b>

2.5 找action方法的對象target
2.5.1 運行時runtime分析

第一步肾扰,進入系統(tǒng)手勢UIScreenEdgePanGestureRecognizer的頭文件,以及其父類蛋逾,父類的父類的頭文件集晚。

/**
 *  進入頭文件的目的是,確認頭文件中關于target的說明区匣,最后在UIGestureRecognizer這個超類中找到了關于initWithTarget的字眼偷拔,說明target很可能是這個類的私有屬性。所以亏钩,我們可以通過KVC來獲取其私有屬性的值莲绰,這樣就完成了找target的任務。問題來了姑丑,通過KVC來獲取值蛤签,首先得知道屬性的名稱呀。所以彻坛,接下來我們要繼續(xù)找到這個屬性的名稱
 */

第二步顷啼,找到target的屬性

/**
 * 1 通過運行時函數(shù),獲取類的屬性列表
   參數(shù):__unsafe_unretained Class cls  表示要獲取哪個類的屬性列表
        unsigned int *outCount 表示屬性列表數(shù)組的個數(shù),傳入的是指針昌屉,函數(shù)進行修改钙蒙,我們就可以通過參數(shù)獲得屬性個數(shù)
   返回值:返回的是屬性列表數(shù)組
 */
unsigned int outCount;
Ivar *ivars = class_copyIvarList([UIGestureRecognizer class], &outCount);


/**
 *  2 遍歷,屬性列表數(shù)組间驮,并打印躬厌,觀察其中的哪個屬性與target有關
 
    結(jié)論:打印結(jié)果第一條,與其有關竞帽。 _targets
 */
for(int i=0;i<outCount;i++)
{
    //2.1 獲取元素(元素就是該類的屬性扛施,是C語言的結(jié)構(gòu)體)
    Ivar ivar = ivars[i];
    //2.2 獲取元素的名稱,通過C語言函數(shù)獲取
    const char *ivar_name =  ivar_getName(ivar);
    //2.3 將C語言字符串包裝成OC字符串屹篓,打印
    NSLog(@"%@",@(ivar_name));
    
}

/**
 *  3 根據(jù)第二步的打印結(jié)果疙渣,通過KVC將_targets的值取出來。并打印堆巧。
 * 
 *  結(jié)論妄荔,打印結(jié)果是一個數(shù)組,且只有一個元素(該元素是一個字典)谍肤,將其取出來
 (
 "(action=handleNavigationTransition:, target=<_UINavigationInteractiveTransition 0x7f8e7b54c540>)"
 )

 */
id _targets = [self.interactivePopGestureRecognizer valueForKeyPath:@"_targets"];
NSLog(@"%@",_targets);


/**
 *  4 將上一步的數(shù)組第一個元素取出來啦租,獲取內(nèi)部字典的target的屬性值--這個屬性值就是我們要找的target對象
 */
NSDictionary *dict = _targets[0];
id temp = [dict valueForKeyPath:@"target"];
NSLog(@"%@",temp);

第三步,自定義手勢
到這里荒揣,我們已經(jīng)找到了action=handleNavigationTransition: target=temp

//1 自定義手勢
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:temp  action:@selector(handleNavigationTransition:)];
//2 添加手勢
[self.view addGestureRecognizer:pan];
2.5.2 根據(jù)自定義手勢的規(guī)律猜測

我們在自定義手勢的時候篷角,一般是這樣的

UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self  action:@selector(panGetsture:)];

也就是說,執(zhí)行手勢的對象一般是self(而對于手勢的監(jiān)控室代理系任,所以是當前手勢的代理)恳蹲。我們可以推測虐块,系統(tǒng)手勢也是這樣的,通過系統(tǒng)手勢的代理來執(zhí)行相對應的方法阱缓。<b style="color:red">所以tartget對象為:self.interactivePopGestureRecognizer.delegate</b>

2.6 問題

因為非凌,目前手勢有我們自己的自定義的举农,我們并沒有判斷當前是否是根控制器荆针,也就是說當前手勢在根控制器也會生效。當我們在根控制器滑動返回的時候颁糟,系統(tǒng)仍然會將當前控制器pop掉航背,但事實上,根控制器是不能被Pop掉的棱貌,不然會出現(xiàn)卡死的現(xiàn)象玖媚。所以,我們需要監(jiān)控手勢的狀態(tài)婚脱,在即將執(zhí)行手勢之前今魔,我們判斷是否為根控制器,如果是障贸,不執(zhí)行手勢错森。所以需要設置自定義手勢的代理為自己。

pan.delegate = self;

//遵守協(xié)議UIGetstureRecognizerDelegate篮洁,實現(xiàn)代理方法
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    //在每次手勢開始之前涩维,調(diào)用這個方法

    if([self.topViewController isEqual:self.viewControllers[0]])
    {
        //如果當前棧頂控制器是根控制器,則返回NO袁波,不執(zhí)行手勢
        return NO ;
    }
    else{
        //如果是非根控制器瓦阐,執(zhí)行首飾
        return  YES;
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市篷牌,隨后出現(xiàn)的幾起案子睡蟋,更是在濱河造成了極大的恐慌,老刑警劉巖枷颊,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件戳杀,死亡現(xiàn)場離奇詭異,居然都是意外死亡偷卧,警方通過查閱死者的電腦和手機豺瘤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來听诸,“玉大人坐求,你說我怎么就攤上這事∩卫妫” “怎么了桥嗤?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵须妻,是天一觀的道長。 經(jīng)常有香客問我泛领,道長荒吏,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任渊鞋,我火速辦了婚禮绰更,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘锡宋。我一直安慰自己儡湾,他們只是感情好,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布执俩。 她就那樣靜靜地躺著徐钠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪役首。 梳的紋絲不亂的頭發(fā)上尝丐,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天,我揣著相機與錄音衡奥,去河邊找鬼爹袁。 笑死,一個胖子當著我的面吹牛杰赛,可吹牛的內(nèi)容都是我干的呢簸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼乏屯,長吁一口氣:“原來是場噩夢啊……” “哼根时!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起辰晕,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蛤迎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后含友,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體替裆,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年窘问,在試婚紗的時候發(fā)現(xiàn)自己被綠了辆童。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡惠赫,死狀恐怖把鉴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情儿咱,我是刑警寧澤庭砍,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布场晶,位于F島的核電站,受9級特大地震影響怠缸,放射性物質(zhì)發(fā)生泄漏诗轻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧权逗,春花似錦、人聲如沸鞠柄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至奉呛,卻和暖如春计螺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瞧壮。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工登馒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人咆槽。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓陈轿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親秦忿。 傳聞我的和親對象是個殘疾皇子麦射,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內(nèi)容