iOS 手勢問題

右滑手勢返回##

原文
ios7開始 蘋果增加了頁面 右滑返回的效果;具體的是以UINavigationController為容器的ViewController間右滑切換頁面。
代碼里的設置是:

 self.navigationController.interactivePopGestureRecognizer.enabled = YES;(default is YES)

可以看到蘋果給navigationController添加了一個手勢(具體為UIScreenEdgePanGestureRecognizer(邊緣手勢,同樣是ios7以后才有的)),就是利用這個手勢實現的 ios7的側滑返回。

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

解決(問題1)辦法:對于這種失效的情況,考慮到interactivePopGestureRecognizer也有delegate屬性,替換默認的self.navigationController.interactivePopGestureRecognizer.delegate來配置右滑返回的表現也是可行的。
我們可以在主NavigationController中設置一下:

  self.navigationController.interactivePopGestureRecognizer.delegate =(id)self

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

解決(問題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

問題三:
UIScrollView上手勢失靈:經研究,發現是UIScrollView上已經添加了 panGestureRecognizer(滑動)手勢

【解決方案】

蘋果以UIGestureRecognizerDelegate的形式,支持多個UIGestureRecognizer共存。其中的一個方法是:
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;
一句話總結就是此方法返回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;    
    }
  }

這種方式就是建一個UISCrollVIew的category,然后我用的時候是全局用的,不知道為什么在快速返回上一層的時候并快速點擊會照成線程卡死。 還有就是兩個手勢共存,所以在返回上一層的過程中,UIScrollerView還會滾動

事實上,對UIGestureRecognizer來說,它們對事件的接收順序和對事件的響應是可以分開設置的,即存在接收鏈和響應鏈。接收鏈如上文所述,和UIView綁定,由UIView的層次決定接收順序。
而響應鏈在apple君的定義下,邏輯出奇的簡單,只有一個方法可以設置多個gestureRecognizer的響應關系:

 - (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;


  //從UINavigationController里得到這個邊緣手勢
  @implementation UINavigationController (iOS7Support)
  @dynamic 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;
  }
  @end
 

 //當screenEdgePanGestureRecognizer生效時,UIScrollerView的panGestureRecognizer失效,這樣就解決了沖突
 UIScreenEdgePanGestureRecognizer *screenEdgePanGestureRecognizer = self.navigationController.screenEdgePanGestureRecognizer;
 [_scrollerView.panGestureRecognizer requireGestureRecognizerToFail:screenEdgePanGestureRecognizer];

全屏的右滑返回##

FDFullscreenPopGesture這個開源項目里用運行時很簡單的實現了全屏的右滑返回。

有人說直接用下面這種kvo的方式就能實現了,這是系統原有的私有屬性,增大那個響應區域就可以了。但我自己還沒實測過,有興趣的可以試試。

[self.interactivePopGestureRecognizer setValue:@([UIScreen mainScreen].bounds.size.width) forKeyPath:@"_recognizer._settings._edgeSettings._edgeRegionSize"];

touchesbegan未響應##

touchesbegan跟UITapGestureRecognizer同時存在時,tap會有一個屬性是cancelsTouchesInView,默認為YES,設置為NO就可以使touchesbegan響應了。

參考:一個Bug引發的對UIGestureRecognizer的思考

參考##

iOS 事件攔截

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容