当用户点击下拉列表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端代码更改
- 注册一个“点击”事件监听器到你的html组件(下拉菜单)。
- 在callback方法中发送通知给本地代码。 例如:“
window.location='fromJavaScript://PopoverIssue';
” - 它会调用uIwebviews
shouldStartLoadWithRequest
本机端代码更改
- 实现
UIPopoverPresentationControllerDelegate
协议的ViewController有uiwebview和超过popoverPresentationControllerShouldDismissPopover popoverPresentationControllerDidDismissPopover
- 将下面的代码放在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]; } }); });
-
执行重写的协议方法如下
-
(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事故的发生。