8

My view hierarchy looks like this:

  • (root) PRSBaseViewController - a UIViewController subclass - has child viewControllers
    • (presented) PRSModalWebViewController - a UINavigationController subclass
      • (pushed, animated:NO) PRSWebViewController - a UIViewController subclass - WKWebView is a subview.

When I try and long press on a link in the WebView I get the error:

Warning: Attempt to present <WKActionSheet: 0x127520d10> on <PRSBaseViewController: 0x1275117f0> whose view is not in the window hierarchy!

Instead of presenting the navigation using presentViewController:animated:completion and instead use the addChildViewController: dance to add it the view controller to the hierarchy. I get no errors, It's quite strange.

Does anyone know what could be causing the view hierarchy issues?

Update: I've made a Gist of all my classes

Shawn Throop
  • 1,281
  • 1
  • 13
  • 28
  • Possibly duplicate of http://stackoverflow.com/questions/26460417/disable-wkactionsheet-on-wkwebview – matt Jan 05 '15 at 03:23
  • 2
    @matt I'm not trying to suppress the share sheets, they're being suppressed against my will. The behavior I don't want and am getting is the answer to his question. – Shawn Throop Jan 05 '15 at 03:26
  • That's the thing, it's Apple's private class. I posted my problem on the developer forums for good measure. Thanks for taking crack at it. – Shawn Throop Jan 05 '15 at 03:33
  • Oh, I see! Sorry about that. So it's something that Apple puts up in response to a long press. I apologize for making you educate me but I'm glad you did! – matt Jan 05 '15 at 03:40
  • So what exactly is `RootViewController`? You say you have put up a Gist of your classes, but no RootViewController is among them. Could you show how RootViewController gets into the view hierarchy and how it acquires its WKWebView subview? (It cannot acquire it in the nib, since there is no WKWebView in the object library; you must be doing all this in code.) – matt Jan 05 '15 at 03:41
  • You apparently mean to use a PRSWebViewController as the root view controller of the UINavigationController, but somehow a RootViewController has sneaked into the story. – matt Jan 05 '15 at 03:47
  • Sorry, I used "RootViewController" as a stand-in for PRSBaseViewController before I had linked to my code, I thought it would be easier to understand, my bad. I've updated my question. – Shawn Throop Jan 05 '15 at 03:52
  • But the error message very clearly says that this _is_ a RootViewController. That's my point. If you were previously using RootViewController, my guess would be that you still have an old version of the storyboard lying around in your intermediate build. I suggest you start by cleaning your caches, as I explain here: http://stackoverflow.com/a/6247073/341994 – matt Jan 05 '15 at 03:54
  • I did a poor editing job, fixed. RootViewController doesn't exist, it was just placeholder text to make clear that it was the rootViewController of my app delegate's window. – Shawn Throop Jan 05 '15 at 03:58
  • 1
    This bug occurs because WebKit is incorrectly attempting to present the `WKActionSheet` on `[view.window rootViewController]` ([source](https://trac.webkit.org/browser/trunk/Source/WebKit2/UIProcess/ios/WKActionSheet.mm?rev=183426#L102)). But if the root view controller is already presenting something then this is wrong. – Lily Ballard May 06 '16 at 21:04

2 Answers2

13

I have come across a similar behavior - and have not figured out how or why this occurs either.

Here is my workaround. Since WKWebView is calling my RootViewController, I'm handling this by overriding RootViewController's presentViewController:animated:completion: - and if the RootViewController already has a presentedViewController, then it forwards the message to that controller. It seems to address the warning message, and gives me the long press menu when using WKWebView inside a modal view.

- (void)presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {

    // Unsure why WKWebView calls this controller - instead of it's own parent controller
    if (self.presentedViewController) {
        [self.presentedViewController presentViewController:viewControllerToPresent animated:flag completion:completion];
    } else {
        [super presentViewController:viewControllerToPresent animated:flag completion:completion];
    }
}

Or in swift:

override func presentViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?) {
    if presentedViewController != nil {
        // Unsure why WKWebView calls this controller - instead of it's own parent controller
        presentedViewController?.presentViewController(viewControllerToPresent, animated: flag, completion: completion)
    } else {
        super.presentViewController(viewControllerToPresent, animated: flag, completion: completion)
    }
}
Kudit
  • 4,212
  • 2
  • 26
  • 32
Todd
  • 962
  • 8
  • 13
  • 1
    This is exactly what I did, I just forgot to answer my own question. – Shawn Throop Apr 29 '15 at 09:27
  • So this fix is showing the actionsheet or its surpressing it? I would like to see action sheet in my app so that I can perform some action – Alok C Aug 10 '15 at 16:02
  • It is works! Great! Add Todd's code to your controller like " " in warning details – Baryon Lee Sep 09 '15 at 15:47
  • This is the workaround I've implemented as well. This issue became apparent in my app when I started seeing crash reports. If the WKWebView is dismissed and the device is subsequently rotated, the issue exhibits as an exception and the app crashes. – isaac Apr 10 '16 at 01:56
4

We've seen this issue in PSPDFKit as well and after investigating the UIKit assembly and WKWebView sources we found a workaround that is still horrible, but not invasive.

The main strategy is to be selective and apply a workaround just in time - then clean up again. You can read the source code here:

https://gist.github.com/steipete/b00fc02aa9f1c66c11d0f996b1ba1265

And please dupe rdar://26295020 so this will get hopefully fixed in time for iOS 10. (The bug exists since iOS 8 and was first reported on iOS 8b5.)

steipete
  • 7,581
  • 5
  • 47
  • 81
  • 1
    This also works with arbitrary long chain of unknown presented view controllers, which we need because we work on a framework, so don’t control all the view controllers in the app. – Douglas Hill May 16 '16 at 09:42