当用户点击下拉列表HTMLselect标签时,ios8 iPad uiwebview在显示popup窗口时崩溃

在ios8和iPad上,如果uiwebview显示包含下拉列表的HTML页面

例如这个页面http://www.w3schools.com/tags/tryit.asp?filename=tryhtml_select

然后

  • 反复点击包含汽车列表的HTML下拉列表。 第一件是沃尔沃。
  • 点击每1/2秒左右uipopover打开和closures
  • 应用程序将崩溃:

终止应用程序,由于未捕获的exception“NSGenericException”,原因:'UIPopoverPresentationController()应该有一个非零的sourceView或barButtonItem在演示之前设置。

反正在uiwebview中解决这个问题吗?

它不会发生使用wkwebview ,但我想解决它在uiwebview

更新:这似乎有助于但不确定的副作用。 我在包含uiwebview的视图控制器中覆盖了以下内容。

 -(void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion { if (completion) { completion(); } [super dismissViewControllerAnimated:NO completion:nil]; } 

问题中提到的解决scheme并没有帮助我,但它确实指出了我正确的方向。 经过一番调查后,我会说这是介绍和消除popover之间的某种竞争条件。 作为一种解决方法,您可以在UIWebView的委托中推迟演示文稿:

 -(void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_USEC), dispatch_get_main_queue(), ^{ [super presentViewController:viewControllerToPresent animated:flag completion:completion]; }); } 

以前的解决scheme并没有帮助我。

有一个bug已经logging到苹果(见openradar )。

问题似乎是Web视图试图在popup窗口中显示视图控制器,而不设置popup窗口的sourceView。 虽然这绝对是一个苹果问题,我已经使用了以下解决方法,以避免我的应用程序崩溃:

 - (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion { // Override this method in the view controller that owns the web view - the web view will try to present on this view controller ;) if (viewControllerToPresent.popoverPresentationController && !viewControllerToPresent.popoverPresentationController.sourceView) { return; } [super presentViewController:viewControllerToPresent animated:flag completion:completion]; } 

我注意到sourceView是在崩溃的情况下设置的,我用下面的方法解决了这个问题:

 -(void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion { UIPopoverPresentationController* pres = viewControllerToPresent.popoverPresentationController; if(pres.sourceView) { //log the fact you are ignoring the call } else { [super presentViewController:viewControllerToPresent animated:flag completion:completion]; } } 

在同样的情况下,我有不同的例外,这里没有任何解决方法帮助我。

这是我的例外:

 Terminating app due to uncaught exception 'NSRangeException', reason: '-[UITableView _contentOffsetForScrollingToRowAtIndexPath:atScrollPosition:]: row (4) beyond bounds (0) for section (0).' 

这是我用来解决它的代码:

 -(void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion { if ([viewControllerToPresent respondsToSelector:NSSelectorFromString(@"_cachedItems")]) { if([viewControllerToPresent valueForKey:@"_cachedItems"] == nil) { if (completion != nil) { completion(); } return; } } [super presentViewController:viewControllerToPresent animated:flag completion:completion]; } 

这是非常讨厌的解决方法,可以防止在即将崩溃的情况下显示下拉菜单,并且此解决scheme可以随时停止工作,因为它使用内部属性。 然而,这是唯一的解决scheme,所以也许这将有助于某人。

我已经减less了这种方式发生崩溃的可能性。使用的JavaScript代码和本机ios

Web端代码更改

  1. 注册一个“点击”事件监听器到你的html组件(下拉菜单)。
  2. 在callback方法中发送通知给本地代码。 例如:“ window.location='fromJavaScript://PopoverIssue';
  3. 它会调用uIwebviews shouldStartLoadWithRequest

本机端代码更改

  1. 实现UIPopoverPresentationControllerDelegate协议的ViewController有uiwebview和超过popoverPresentationControllerShouldDismissPopover popoverPresentationControllerDidDismissPopover
  2. 将下面的代码放在uiwebview的shouldStartLoadWithRequest方法中用于上面的点击通知
     [[UIApplication sharedApplication] beginIgnoringInteractionEvents]; self.popoverPresentationController = self.presentedViewController.popoverPresentationController; self.existedPopoverDelegate = [self.popoverPresentationController delegate]; self.popoverPresentationController.delegate = self; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); dispatch_async(queue, ^{ int64_t delay = 2.0; dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, delay * NSEC_PER_SEC); dispatch_after(time, dispatch_get_main_queue(), ^{ if([[UIApplication sharedApplication] isIgnoringInteractionEvents]) { [[UIApplication sharedApplication] endIgnoringInteractionEvents]; } }); }); 
  3. 执行重写的协议方法如下

    
    
    

    • (BOOL)popoverPresentationControllerShouldDismissPopover 🙁 UIPopoverPresentationController *)popoverPresentationController
      {
      [self.existedPopoverDelegate popoverPresentationControllerShouldDismissPopover:popoverPresentationController];
      返回YES;
      }

    • (void)popoverPresentationControllerDidDismissPopover:(UIPopoverPresentationController *)popoverPresentationController
      {
      [self.existedPopoverDelegate popoverPresentationControllerDidDismissPopover:popoverPresentationController];
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
      dispatch_async(queue,^ {
      int64_t delay = 2.0;
      dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW,delay * NSEC_PER_SEC);
      dispatch_after(time,dispatch_get_main_queue(),^ {
      if([[UIApplication sharedApplication] isIgnoringInteractionEvents])
      {
      [[UIApplication sharedApplication] endIgnoringInteractionEvents];
      }
      });
      });
      }

希望这将有助于减less事故的发生。