iOS系統中,滑動返回手勢,其實是一個UIPanGestureRecognizer,系統默認的操作是只有滑動屏幕的左邊的某個位置,UIPanGestureRecognizer才會起作用。UIScrollView的滑動手勢也是UIPanGestureRecognizer。UIGestureRecognizer和UIView是多對一的關系(具體點這裡),UIGestureRecognizer一定要和view進行綁定才能發揮作用。因此不難想象,UIGestureRecognizer對於屏幕上的手勢事件,其接收順序和UIView的層次結構是一致的
UINavigationController.view —> UIViewController.view —> UIScrollView —> Screen and User's finger
即UIScrollView的panGestureRecognizer先接收到了手勢事件,直接就地處理而沒有往下傳遞。
實際上這就是兩個panGestureRecognizer共存的問題。
由於scrollView的滑動手勢攔截了事件,那我重寫scrollView中panGestureRecognizer的代理方法,讓它不攔截就好了嘛。於是繼承UIScrollView,重寫下面的方法。
//一句話總結就是此方法返回YES時,手勢事件會一直往下傳遞,不論當前層次是否對該事件進行響應。 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { if ([self panBack:gestureRecognizer]) { return YES; } return NO; } //location_X可自己定義,其代表的是滑動返回距左邊的有效長度 - (BOOL)panBack:(UIGestureRecognizer *)gestureRecognizer { int location_X = 100; if (gestureRecognizer == self.panGestureRecognizer) { UIPanGestureRecognizer *pan = (UIPanGestureRecognizer *)gestureRecognizer; CGPoint point = [pan translationInView:self]; UIGestureRecognizerState state = gestureRecognizer.state; if (UIGestureRecognizerStateBegan == state || UIGestureRecognizerStatePossible == state) { CGPoint location = [gestureRecognizer locationInView:self]; if (point.x > 0 && location.x < location_X && self.contentOffset.x <= 0) { return YES; } } } return NO;}
需要側邊滑動時 panBack 返回YES,這時候,我讓scrollView的手勢和頁面的滑動返回手勢共存,scrollView不攔截手勢,那不就可以滑動返回了嗎。好了,測試一下,可以滑動返回,但是滑動返回時,scrollView也跟著在滑動呢,我們應該讓scrollView切換的時候相應panGesture,滑動返回的時候不響應,那重寫scrollView中的另外一個panGestureRecognizer的代理方法。
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { if ([self panBack:gestureRecognizer]) { return NO; } return YES; }
以上的代碼都是在一個自定義的UIScrollView上的,重寫上面的方法即可。然後讓橫向滾動的scrollView繼承這個自定義UIScrollView就OK了。
另外要注意重寫的自定義的UIScrollView要記得實現協議UIGestureRecognizerDelegate
原理:
scrollView的pan手勢會讓系統的pan手勢失效,所以我們只需要在系統手勢失效且scrollView的位置在初始位置的時候讓兩個手勢同時啟用就可以了。