【疑難雜症】interactivePopGestureRecognizer 致使頁面卡死

記錄一個很是奇怪的 bug(參考地址ios

interactivePopGestureRecognizer 的使用

interactivePopGestureRecognizer 是系統爲 UINavigationController 添加的右滑 pop 手勢,由系統提供返回動畫,實現邊緣返回功能。git

@property(nullable, nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer API_AVAILABLE(ios(7.0)) API_UNAVAILABLE(tvos);
複製代碼

navigation controller 會將手勢加在其 view 上,並負責將最上層 ViewController 從 navigation stack 中移除。github

由於這個手勢是加在 navigation controller 上的,手勢的 enable 狀態是 stack 中全部 ViewController 共享的,所以若是咱們想針對某個 VC 禁用 interactivePopGestureRecognizer,咱們可能一般會這樣作 ---- 在 willAppear 和 willDisappear 的生命週期中進行設置:app

- (void)viewWillAppear:(BOOL)animated
{
  [super viewWillAppear:animated];   
  self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
- (void)viewWillDisappear:(BOOL)animated
{
  [super viewWillDisappear:animated];
  self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}
複製代碼

頁面卡死的產生

這樣的作法會致使一個很是嚴重的卡死 bug,具體路徑以下:動畫

  1. FirstViewController 在 willAppear 時設置手勢 enable = NO;atom

  2. [FirstViewController push: SecondViewController]spa

  3. FirstViewController 在 WillDisappear 時設置手勢 enable = YES;code

  4. 在 SecondViewController 中嘗試使用邊緣手勢,頁面卡死,沒法響應任何手勢生命週期

爲何在新 push 出來的 SecondViewController 中嘗試使用邊緣手勢就會形成卡死呢,緣由在於此時同時觸發了兩件事情:get

  1. 邊緣手勢 interactivePopGestureRecognizer 開始響應,將 SecondViewController 從 navigation stack 中移除

  2. FirstViewController willAppear 被調用,interactivePopGestureRecognizer.enabled = NO,那麼正在響應的手勢操做將會被中斷掉。

@property(nonatomic, getter=isEnabled) BOOL enabled; // default is YES. disabled gesture recognizers will not receive touches. when changed to NO the gesture recognizer will be cancelled if it's currently recognizing a gesture

若是設置爲 NO,將 cancel 正在響應的手勢

這樣的結果就是系統的 interactivePopGestureRecognizer 生命週期被破壞,SecondViewController 被從 stack 中移除,可是其 view 卻仍留在最頂層,而且沒法再響應任何手勢。這樣的現象就是看起來什麼都沒有發生,可是 SecondViewController 實際上已經被移除了,表現爲頁面直接卡死。

兩種解決方式

知道了緣由以後解決方案就變得很是簡單了,延緩手勢被禁用的時機便可。在 DidAppear 的時候禁用,就能夠避免上述的衝突:

- (void)viewDidAppear:(BOOL)animated
{
  [super viewDidAppear:animated];
  self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
複製代碼

固然,也能夠經過其餘的方式在 FirstViewController 中禁用邊緣手勢,好比經過 shouldBegin 方法過濾,這樣也能避免衝突:

此方案參考:stackoverflow.com/a/21424580

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
  if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)] &&
      gestureRecognizer == self.navigationController.interactivePopGestureRecognizer) {
    return NO;
  }
  return YES;
}
複製代碼
相關文章
相關標籤/搜索