iOS7下滑動返回與ScrollView共存二三事

來源:互聯網
上載者:User

標籤:style   blog   class   code   java   tar   javascript   color   get   strong   int   

【轉載請註明出處】 = =不是整篇複製就算註明出處了親。。。

 

iOS7下滑動返回與ScrollView共存二三事

 

【前情回顧】

去年的時候,寫了這篇文章iOS7滑動返回。文中提到,對於多頁面結構的應用,可以替換interactivePopGestureRecognizer的delegate以統一管理應用中所有頁面滑動返回的開關,比如在UINavigationController的衍生類別中

 1 //我是一個NavigationController的衍生類別 2 - (id)initWithRootViewController:(UIViewController *)rootViewController 3 { 4     self = [super initWithRootViewController:rootViewController]; 5     if (self) 6     { 7         //在naviVC中統一處理棧中各個vc是否支援滑動返回的情況 8         //當前僅最底層的vc關閉滑動返回 9         self.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;10     }11     12     return self;13 }

然後在委託中控制滑動返回的開關

 1 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer 2 { 3     if (self.viewControllers.count == 1)//關閉主介面的右滑返回 4     { 5         return NO; 6     } 7     else 8     { 9         return YES;10     }11 }

 

【問題所在】

看上去挺美好的。。。。直到遇到了ScrollView。

替換了delegate後,在使用時ScrollView,在螢幕左邊緣就無法觸發滑動返回效果了,

 

【問題原因】

滑動返回事實上也是由存在已久的UIPanGestureRecognizer來識別並響應的,它直接與UINavigationController的view(方便起見,下文中以UINavigationController.view表示)進行綁定,因此中存在如下關係:
UIPanGestureRecognizer          ——bind——  UIScrollView

UIScreenEdgePanGestureRecognizer ——bind——  UINavigationController.view

滑動返回無法觸發,說明UIScreenEdgePanGestureRecognizer並沒有接收到手勢事件。

 

根據apple君的官方文檔,UIGestureRecognizer和UIView是多對一的關係(具體點這裡),UIGestureRecognizer一定要和view進行綁定才能發揮作用。因此不難想象,UIGestureRecognizer對於螢幕上的手勢事件,其接收順序和UIView的階層是一致的。同樣以為例

 

(我是Z軸)-------------------------------------------------------------------------------------------------------------------------------------->

 

UINavigationController.view —>  UIViewController.view —>  UIScrollView —>  Screen and User‘s finger

 

即UIScrollView的panGestureRecognizer先接收到了手勢事件,直接就地處理而沒有往下傳遞。

 

實際上這就是兩個panGestureRecognizer共存的問題。

 

【解決方案】

蘋果以UIGestureRecognizerDelegate的形式,支援多個UIGestureRecognizer共存。其中的一個方法是:

1 // called when the recognition of one of gestureRecognizer or otherGestureRecognizer would be blocked by the other2 // 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 YES5 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;

 一句話總結就是此方法返回YES時,手勢事件會一直往下傳遞,不論當前層次是否對該事件進行響應。

 

看看UIScrollView的標頭檔,有如下描述:

1 // Use these accessors to configure the scroll view‘s built-in gesture recognizers.2 // Do not change the gestures‘ delegates or override the getters for these properties.3 @property(nonatomic, readonly) UIPanGestureRecognizer *panGestureRecognizer NS_AVAILABLE_IOS(5_0);

UIScrollView本身是其panGestureRecognizer的delegate,且apple君明確表明不能修改它的delegate(修改的時候也會有警告)。

那麼只好曲線救國。

UIScrollView作為delegate,說明UIScrollView中實現了上文提到的shouldRecognizeSimultaneouslyWithGestureRecognizer方法,返回了NO。建立一個UIScrollView的category,由於category中的同名方法會覆蓋原有.m檔案中的實現,使得可以自訂手勢事件的傳遞,如下:

 1 @implementation UIScrollView (AllowPanGestureEventPass) 2  3 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer 4 { 5     if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]] 6         && [otherGestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]]) 7     { 8         return YES; 9     }10     else11     {12         return  NO;13     }14 }

再次運行demo,看看效果:

嗯,滑動返回已經成功觸發,鼓掌!

等會等會。。。

好像不太對。。。

scrollView怎麼也滾動了!!!!!

O。。。只是做到了將手勢事件往下傳遞,而沒有關閉掉在邊緣時UIScrollView對事件的響應。

 

事實上,對UIGestureRecognizer來說,它們對事件的接收順序和對事件的響應是可以分開設定的,即存在接收鏈和響應鏈。接收鏈如上文所述,和UIView綁定,由UIView的層次決定接收順序。

而響應鏈在apple君的定義下,邏輯出奇的簡單,只有一個方法可以設定多個gestureRecognizer的響應關係:

// 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都是一個有限狀態機器,上述方法會在兩個gestureRecognizer間建立一個依託於state的依賴關係,當被依賴的gestureRecognizer.state = failed時,另一個gestureRecognizer才能對手勢進行響應。

 

所以,只需要

[_scrollView.panGestureRecognizer requireGestureRecognizerToFail:screenEdgePanGestureRecognizer];

即可。

 

再次運行demo,看看效果:

 Works like a Charm!!

 

P.S:screenEdgePanGestureRecognizer是和UINavigationController.view綁定的,因此可以遍曆UINavigationController.view.gestureRecognizers來擷取,如下:

- (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;}

 


demo地址:https://github.com/cDigger/CoExistOfScrollViewAndBackGesture/tree/master

 

【總結】

寫了這麼多,只是為了最初統一管理滑動返回的一點點便利,似乎很有些得不償失。

我並不建議直接在項目中使用這種非常規手段。

但使用apple君提供的積木,自己拼出系統中的新功能,也是iOS開發的樂趣之一啊。

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.