記錄一個很是奇怪的 bug(參考地址)ios
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,具體路徑以下:動畫
FirstViewController 在 willAppear 時設置手勢 enable = NO;atom
[FirstViewController push: SecondViewController]spa
FirstViewController 在 WillDisappear 時設置手勢 enable = YES;code
在 SecondViewController 中嘗試使用邊緣手勢,頁面卡死,沒法響應任何手勢生命週期
爲何在新 push 出來的 SecondViewController 中嘗試使用邊緣手勢就會形成卡死呢,緣由在於此時同時觸發了兩件事情:get
邊緣手勢 interactivePopGestureRecognizer 開始響應,將 SecondViewController 從 navigation stack 中移除
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 方法過濾,這樣也能避免衝突:
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)] &&
gestureRecognizer == self.navigationController.interactivePopGestureRecognizer) {
return NO;
}
return YES;
}
複製代碼