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

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

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

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

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

解決(問題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
借鑒了別人的方法:具體是通過 獲取當(dāng)前pushView棧里的當(dāng)前顯示的VC,根據(jù)這個(gè)VC來(lái)決定 是否開啟手勢(shì)(如果currentShowVC 是當(dāng)前顯示的,則開啟手勢(shì);如果 currentShowVC為nil,則代表在主頁(yè)面,關(guān)閉手勢(shì))
注:
當(dāng)時(shí)試了一種方法 就是滑動(dòng)的時(shí)候來(lái)回設(shè)置 interactivePopGestureRecognizer的delegate;發(fā)現(xiàn) 會(huì)有crash,原因就是 因?yàn)?我把 當(dāng)前顯示的VC設(shè)置為了這個(gè)手勢(shì)的delegate,但當(dāng)這個(gè)VC消失的時(shí)候,這個(gè)delegate便被釋放了,導(dǎo)致crash

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

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

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

解決(問題三)辦法:
參考:http://www.cnblogs.com/lexingyu/p/3702742.html
【解決方案】
蘋果以UIGestureRecognizerDelegate的形式,支持多個(gè)UIGestureRecognizer共存。其中的一個(gè)方法是:
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時(shí),手勢(shì)事件會(huì)一直往下傳遞,不論當(dāng)前層次是否對(duì)該事件進(jìn)行響應(yīng)。
    @implementation UIScrollView (AllowPanGestureEventPass)

  • (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
    {
    if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]
    && [otherGestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]])
    {
    return YES;
    }
    else
    {
    return NO;
    }
    }
    事實(shí)上,對(duì)UIGestureRecognizer來(lái)說,它們對(duì)事件的接收順序和對(duì)事件的響應(yīng)是可以分開設(shè)置的,即存在接收鏈和響應(yīng)鏈。接收鏈如上文所述,和UIView綁定,由UIView的層次決定接收順序。
    而響應(yīng)鏈在apple君的定義下,邏輯出奇的簡(jiǎn)單,只有一個(gè)方法可以設(shè)置多個(gè)gestureRecognizer的響應(yīng)關(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;
    每個(gè)UIGesturerecognizer都是一個(gè)有限狀態(tài)機(jī),上述方法會(huì)在兩個(gè)gestureRecognizer間建立一個(gè)依托于state的依賴關(guān)系,當(dāng)被依賴的gestureRecognizer.state = failed時(shí),另一個(gè)gestureRecognizer才能對(duì)手勢(shì)進(jìn)行響應(yīng)。
    所以,只需要
    [_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下滑動(dòng)返回與ScrollView共存
http://www.cnblogs.com/lexingyu/p/3702742.html
更牛逼 解決了interactivePopGestureRecognizer.delegate 的 釋放導(dǎo)致crash的問題
http://www.2cto.com/kf/201401/272886.htm

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

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

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

1 self.navigationController.interactivePopGestureRecognizer.enabled = YES;
默認(rèn)情況下enabled為YES。

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

解決方案:比較直觀的辦法是在自定義返回按鈕時(shí),使用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

但這樣無(wú)法支持左上角多個(gè)按鈕的情況。考慮到 interactivePopGestureRecognizer也有delegate屬性, 替換默認(rèn)的 self . navigationController .interactivePopGestureRecognizer.delegate來(lái)配置右滑返回的表現(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右滑返回的開啟,而不需要在各個(gè)ViewController中分別設(shè)置enabled。

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

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

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

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

3、在使用右滑返回拖動(dòng)到一半時(shí),有時(shí)會(huì)在導(dǎo)航欄上看到三個(gè)排成一行的小藍(lán)點(diǎn)。

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

P.S:在一個(gè)帖子上看到一個(gè)辦法:

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

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

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

_UINavigationInteractiveTransition實(shí)例,該類聲明如下:

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)部,實(shí)際上是一個(gè)UIScreenEdgePanGestureRecognizer實(shí)例在起作用,它是iOS7中引入的一個(gè)新類,用于支持某些情況下ViewController間切換的初始化。apple官方文檔中對(duì)其的描述很少,如下:

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

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

https://github.com/vinqon/MultiLayerNavigation

P.S:對(duì)于一些特殊的需求,如在有ScrollView的界面上(比如瀏覽照片)模擬右滑返回,當(dāng)滑動(dòng)到最左邊時(shí)即執(zhí)行右滑返回,該類無(wú)法滿足,待處理【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、自定義返回按鈕時(shí),iOS7手勢(shì)返回遇到的問題

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)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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