navigationController側(cè)滑

#import "STKNavigationController.h"

@interface STKNavigationController ()<UINavigationControllerDelegate>
@property(nonatomic,weak) UIViewController* currentShowVC;
@end

@implementation STKNavigationController
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController{
    if (self = [super initWithRootViewController:rootViewController]) {
        STKNavigationController *nav = [super initWithRootViewController:rootViewController];
        nav.interactivePopGestureRecognizer.enabled = YES;
        nav.interactivePopGestureRecognizer.delegate = self;
        nav.delegate = self;
    }
    return self;
}
- (void)viewDidLoad {
    [super viewDidLoad];
//    __weak typeof(self) weakSelf = self;
//    if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)]) {
//        self.interactivePopGestureRecognizer.delegate = weakSelf;
//    }
}
#pragma mark UINavigationControllerDelegate

-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
    
}

-(void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
    
    if (navigationController.viewControllers.count == 1)
        self.currentShowVC = nil;
    else
        self.currentShowVC = viewController;
}

#pragma mark UIGestureRecognizerDelegate
-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
    
    
    if (gestureRecognizer == self.interactivePopGestureRecognizer) {
        return (self.currentShowVC == self.topViewController); //the most important
    }
    return YES;
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] && [otherGestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]]) {
        return YES;
    } else {
        return NO;
    }
}

前言:
ios7開始 蘋果增加了頁面 右滑返回的效果;具體的是以UINavigationController為容器的ViewController間右滑切換頁面。
代碼里的設置是:
self.navigationController.interactivePopGestureRecognizer.enabled = YES;(default is YES)
可以看到蘋果給navigationController添加了一個手勢(具體為UIScreenEdgePanGestureRecognizer(邊緣手勢,同樣是ios7以后才有的)),就是利用這個手勢實現(xiàn)的 ios7的側(cè)滑返回。

問題1:
然而事情并非我們想的那么簡單。
1.當我們用系統(tǒng)的UINavigationController,并且也是利用系統(tǒng)的navigateBar的時候,是完全沒有問題的
2.但是當我們沒有用系統(tǒng)的navigateBar或者自定義了返回按鈕的時候,這個時候 右滑返回是失效的。

解決(問題1)辦法:
對于這種失效的情況,考慮到interactivePopGestureRecognizer也有delegate屬性,替換默認的self.navigationController.interactivePopGestureRecognizer.delegate來配置右滑返回的表現(xiàn)也是可行的。

我們可以在主NavigationController中設置一下:
self.navigationController.interactivePopGestureRecognizer.delegate =(id)self

問題2:
但是出現(xiàn)很多問題,比如說在rootViewController的時候這個手勢也可以響應,導致整個程序頁面不響應;push了多層后,快速的觸發(fā)兩次手勢,也會錯亂

解決(問題2)辦法:
@interface NavRootViewController : UINavigationController
@property(nonatomic,weak) UIViewController* currentShowVC;
@end
  
@implementation NavRootViewController
-(id)initWithRootViewController:(UIViewController )rootViewController
{
NavRootViewController
nvc = [super initWithRootViewController:rootViewController];
self.interactivePopGestureRecognizer.delegate = self;
nvc.delegate = self;
return nvc;
}

-(void)navigationController:(UINavigationController *)navigationController willShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
}
-(void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if (navigationController.viewControllers.count == 1)
self.currentShowVC = Nil;
else
self.currentShowVC = viewController;
}

-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if (gestureRecognizer == self.interactivePopGestureRecognizer) {
return (self.currentShowVC == self.topViewController); //the most important
}
return YES;
}

@end
借鑒了別人的方法:具體是通過 獲取當前pushView棧里的當前顯示的VC,根據(jù)這個VC來決定 是否開啟手勢(如果currentShowVC 是當前顯示的,則開啟手勢;如果 currentShowVC為nil,則代表在主頁面,關(guān)閉手勢)
注:
當時試了一種方法 就是滑動的時候來回設置 interactivePopGestureRecognizer的delegate;發(fā)現(xiàn) 會有crash,原因就是 因為 我把 當前顯示的VC設置為了這個手勢的delegate,但當這個VC消失的時候,這個delegate便被釋放了,導致crash

至此,覺得ios7上的右滑返回大功告成了,心里正happy,媽蛋,發(fā)現(xiàn)了一個可恥的bug:
UIScrollView 上 右滑返回的手勢失靈了,靠?。。。。?!

問題三:
UIScrollView上手勢失靈:
經(jīng)研究,發(fā)現(xiàn)是UIScrollView上已經(jīng)添加了 panGestureRecognizer(滑動)手勢

ios7 <wbr>側(cè)滑返回

解決(問題三)辦法:
參考:http://www.cnblogs.com/lexingyu/p/3702742.html
【解決方案】
蘋果以UIGestureRecognizerDelegate的形式,支持多個UIGestureRecognizer共存。其中的一個方法是:
1 // called when the recognition of one of gestureRecognizer or otherGestureRecognizer would be blocked by the other
2 // return YES to allow both to recognize simultaneously. the default implementation returns NO (by default no two gestures can be recognized simultaneously)
3 //
4 // note: returning YES is guaranteed to allow simultaneous recognition. returning NO is not guaranteed to prevent simultaneous recognition, as the other gesture's delegate may return YES
5

  • (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
    一句話總結(jié)就是此方法返回YES時,手勢事件會一直往下傳遞,不論當前層次是否對該事件進行響應。
    @implementation UIScrollView (AllowPanGestureEventPass)

  • (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
    {
    if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]
    && [otherGestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]])
    {
    return YES;
    }
    else
    {
    return NO;
    }
    }
    事實上,對UIGestureRecognizer來說,它們對事件的接收順序和對事件的響應是可以分開設置的,即存在接收鏈和響應鏈。接收鏈如上文所述,和UIView綁定,由UIView的層次決定接收順序。
    而響應鏈在apple君的定義下,邏輯出奇的簡單,只有一個方法可以設置多個gestureRecognizer的響應關(guān)系:
    // create a relationship with another gesture recognizer that will prevent this gesture's actions from being called until otherGestureRecognizer transitions to UIGestureRecognizerStateFailed // if otherGestureRecognizer transitions to UIGestureRecognizerStateRecognized or UIGestureRecognizerStateBegan then this recognizer will instead transition to UIGestureRecognizerStateFailed // example usage: a single tap may require a double tap to fail - (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;
    每個UIGesturerecognizer都是一個有限狀態(tài)機,上述方法會在兩個gestureRecognizer間建立一個依托于state的依賴關(guān)系,當被依賴的gestureRecognizer.state = failed時,另一個gestureRecognizer才能對手勢進行響應。
    所以,只需要
    [_scrollView.panGestureRecognizer requireGestureRecognizerToFail:screenEdgePanGestureRecognizer];

  • (UIScreenEdgePanGestureRecognizer *)screenEdgePanGestureRecognizer
    {
    UIScreenEdgePanGestureRecognizer *screenEdgePanGestureRecognizer = nil;
    if (self.view.gestureRecognizers.count > 0)
    {
    for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers)
    {
    if ([recognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]])
    {
    screenEdgePanGestureRecognizer = (UIScreenEdgePanGestureRecognizer *)recognizer;
    break;
    }
    }
    }

return screenEdgePanGestureRecognizer;
}
參考:
牛逼 解決了iOS7下滑動返回與ScrollView共存
http://www.cnblogs.com/lexingyu/p/3702742.html
更牛逼 解決了interactivePopGestureRecognizer.delegate 的 釋放導致crash的問題
http://www.2cto.com/kf/201401/272886.htm

\\\\\\\\\\\\\\\\\\\\\\\\\
iOS 7中在傳統(tǒng)的左上角返回鍵之外,提供了右滑返回上一級界面的手勢。支持此手勢的是UINavigationController中新增的屬性

interactivePopGestureRecognizer,即右滑返回只支持以UINavigationController為容器的ViewController間切換,要想在自定義容器中使用,需要一些額外的工作。

基本地,控制ViewController是否啟用右滑返回,只需要這樣:

1 self.navigationController.interactivePopGestureRecognizer.enabled = YES;
默認情況下enabled為YES。

在實際使用中,遇到了一些問題,整理如下:
1、自定義返回按鈕后,右滑返回失效;

解決方案:比較直觀的辦法是在自定義返回按鈕時,使用backBarButtonItem:

1 UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
2 //some initialize code here...
3 UIBarButtonItem *barItem = [[UIBarButtonItem alloc] initWithCustomView:backButton];
4 self.navigationItem.leftBarButtonItem = barItem; //not working
5 self.navigationItem.backBarButtonItem = barItem; //serve well
P.S:關(guān)于backBarButtonItem和leftBarButtonItem的區(qū)別:

http://www.cocoachina.com/ask/questions/show/97110

但這樣無法支持左上角多個按鈕的情況??紤]到 interactivePopGestureRecognizer也有delegate屬性, 替換默認的 self . navigationController .interactivePopGestureRecognizer.delegate來配置右滑返回的表現(xiàn)也是可行的。在主ViewController中:

1   self.navigationController.interactivePopGestureRecognizer.delegate = self;
1   - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
2   {
3   if (self.navigationController.viewControllers.count == 1)//關(guān)閉主界面的右滑返回
4   {
5   return NO;
6   }
7   else
8   {
9   return YES;
10   }
11   }
如此做的好處是可以在主ViewController中配置棧中所有ViewController右滑返回的開啟,而不需要在各個ViewController中分別設置enabled。

值得注意的是:在替換了delegate之后,必須在gestureRecognizerShouldBegin:中設置某ViewController A開啟右滑返回,同時在A中未設置interactivePopGestureRecognizer.enabled = NO,右滑返回才會開啟,即二者中任一為NO,右滑返回都處于關(guān)閉狀態(tài)。

2、主界面(UINavigationController棧中的第一個ViewController)默認也是開啟右滑返回的。若在主界面上右滑,不會有動作執(zhí)行。但此時想進入下一級ViewController(如點擊tableView中某一行),切換動畫卻沒有出現(xiàn)。切回桌面再進入應用,發(fā)現(xiàn)直接進入了下一級ViewController。

解決方案:這個問題是在最初試驗右滑返回的使用方式時出現(xiàn)的。在使用自定義返回按鈕的ViewController中

1 self.navigationController.interactivePopGestureRecognizer.delegate = self;
解決解決問題1的同時,造成了問題2。和1中相似,都是在替換了默認的delegate之后,interactivePopGestureRecognizer就能調(diào)用自定義的返回方法了。具體原因尚不清楚,待更新【Mark】。

3、在使用右滑返回拖動到一半時,有時會在導航欄上看到三個排成一行的小藍點。

解決方案:原因不明,解決方案不明。

P.S:在一個帖子上看到一個辦法:

1   self.navigationItem.title = @"";
可以隱藏小藍點,但由于小藍點非必現(xiàn),在不明究竟的情況下很難說是否有效。

帖子鏈接: http://www.tuicool.com/articles/FB3IJ3

(1)在工程中查看, self . navigationController .interactivePopGestureRecognizer.delegate實際上是一個

_UINavigationInteractiveTransition實例,該類聲明如下:

1   @class UIScreenEdgePanGestureRecognizer;
2
3   @interface _UINavigationInteractiveTransition : _UINavigationInteractiveTransitionBase {
4   UIScreenEdgePanGestureRecognizer *_edgePanRecognizer;
5   }
6
7   @property(readonly) UIScreenEdgePanGestureRecognizer * screenEdgePanGestureRecognizer;
8
9   - (void)_configureNavigationGesture;
10   - (BOOL)_gestureRecognizer:(id)arg1 shouldBeRequiredToFailByGestureRecognizer:(id)arg2;
11   - (void)dealloc;
12   - (BOOL)gestureRecognizer:(id)arg1 shouldReceiveTouch:(id)arg2;
13   - (BOOL)gestureRecognizer:(id)arg1 shouldRecognizeSimultaneouslyWithGestureRecognizer:(id)arg2;
14   - (BOOL)gestureRecognizerShouldBegin:(id)arg1;
15   - (id)gestureRecognizerView;
16   - (id)initWithViewController:(id)arg1 animator:(id)arg2;
17   - (id)screenEdgePanGestureRecognizer;
18   - (void)setNotInteractiveTransition;
19   - (void)startInteractiveTransition;
20
21   @end
可以看到,委托的內(nèi)部,實際上是一個UIScreenEdgePanGestureRecognizer實例在起作用,它是iOS7中引入的一個新類,用于支持某些情況下ViewController間切換的初始化。apple官方文檔中對其的描述很少,如下:

A UIScreenEdgePanGestureRecognizer looks for panning (dragging) gestures that start near an edge of the screen. The system uses screen edge gestures in some cases to initiate view controller transitions. You can use this class to replicate the same gesture behavior for your own actions.

After creating a screen edge pan gesture recognizer, assign an appropriate value to the edges property before attaching the gesture recognizer to your view. You use this property to specify from which edges the gesture may start. This gesture recognizer ignores any touches beyond the first touch.

要在自定義的ViewController容器中支持右滑返回,可能就需要用到它。

(2)目前不少應用還是用的iOS 6.1 SDK,而許多iOS7的用戶對右滑返回的需求非常迫切,因此在iOS 6.1SDK下模擬右滑返回在短時間內(nèi)是有必要的,以下是一個通過在push時截取上級ViewController界面為UIImage作為下一級ViewController的背景的一種實現(xiàn)方式:

作者的本意似乎并不要要模擬右滑返回,但稍作修改就能在結(jié)構(gòu)比較簡單的應用中使用,以下是鏈接:

https://github.com/vinqon/MultiLayerNavigation

P.S:對于一些特殊的需求,如在有ScrollView的界面上(比如瀏覽照片)模擬右滑返回,當滑動到最左邊時即執(zhí)行右滑返回,該類無法滿足,待處理【Mark】。

1、UIScreenEdgePanGestureRecognizer Class Reference

https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIScreenEdgePanGestureRecognizer_class/Reference/Reference.html#//apple_ref/occ/cl/UIScreenEdgePanGestureRecognizer

2、_UINavigationInteractiveTransition.h

https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/UIKit.framework/_UINavigationInteractiveTransition.h

3、自定義返回按鈕時,iOS7手勢返回遇到的問題

http://www.tuicool.com/articles/FB3IJ3

http://www.tuicool.com/articles/vMfAVv

4、餅狀圖

http://blog.csdn.net/kmyhy/article/details/7819728

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

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