【疑难杂症】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;
}
复制代码
相关文章
相关标签/搜索