记录一个很是奇怪的 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;
}
复制代码