Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WKWebView action sheet dismisses the presenting view controller after being dismissed

I have a view controller (WKWebViewController) that is embedded in a NavigationController. This ViewController presents a WKWebView. After navigating to any web page; and upon long-pressing any detected content, such as a phone number or a link, an action sheet is displayed with options like copy, share, etc. The issue is when this action sheet is dismissed, the WKWebViewController gets dismissed along with it and the root ViewController is displayed! Regardless of the what the selection was be it Copy, Cancel, or even if tapped anywhere on the screen.

I've tried overriding the "present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil)" and the "dismiss(animated flag: Bool, completion: (() -> Void)?)" in an attempt to understand what is happening but then realized that the action sheet is not being presented neither dismissed by its parent view controller (WKWebViewController), the matter of fact I did the same on the root view controller and found that it is not presented on it neither.

I've done a lot of searching trying to understand what is causing this behavior, I even built a new project with a simple WKWebView only and always ended up with the same problem.

Here is the code:

import UIKit; import WebKit
class WKWebViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {

var destinationUrlString: String?
var myWebView: WKWebView!

override func viewDidLoad() {
    super.viewDidLoad()

    let webConfiguration = WKWebViewConfiguration()
    webConfiguration.dataDetectorTypes = []
    let origin = CGPoint(x: 0, y: 0)
    let size  = CGSize(width: view.frame.size.width, height: view.frame.size.height)
    myWebView = WKWebView(frame: .init(origin: origin, size: size), configuration: webConfiguration)
    myWebView.uiDelegate = self
    myWebView.navigationDelegate = self
    myWebView.allowsLinkPreview = false
    view = myWebView

    destinationUrlString = "https://www.stackoverflow.com"
    guard let url = URL(string: destinationUrlString!) else {return}
    print(url)
    let request = URLRequest(url: url)
    myWebView.load(request)
}
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
    //show progress indicator
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    //dismiss progress indicator
}
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
    //show error
}
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
    //show error
}
}

I've also attached a GIF showing the issue:

enter image description here

I am using Xcode 9.3 (9E145) and Swift 4.1.

Am I missing something? How can this be fixed? Any help would be really appreciated.

like image 360
ferasfa Avatar asked Apr 16 '18 11:04

ferasfa


People also ask

How to disable interactive swipe to dismiss for view controllers?

Fortunately, UIKit has a dedicated property that deactivates the swipe to dismiss behavior: isModalInPresentation . This is false by default, but if you set it to true then UIKit will stop the interactive dismiss gesture and also ignore any events that occur outside of the detail view controller's bounds.

What is UIViewController in IOS?

The UIViewController class defines the shared behavior that's common to all view controllers. You rarely create instances of the UIViewController class directly. Instead, you subclass UIViewController and add the methods and properties needed to manage the view controller's view hierarchy.


2 Answers

I've experienced the same problem.

To deal with this situation, in the root view controller of the UIViewController's hierarchy (in your case this will be the "Root View Controller" — note, in many cases it might be a UINavgiationController which you'd then have to subclass) override dismissViewControllerAnimated:completion: to deal with the WKWebView calling dismiss multiple times.

Objective-C

@property (weak) UIViewController *lastPresentedController;

- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion
{
    // WKWebView actions sheets workaround
    if (self.presentedViewController && self.lastPresentedController != self.presentedViewController ) {
        self.lastPresentedController = self.presentedViewController;
        [self.presentedViewController dismissViewControllerAnimated:YES completion:^{
            if( completion ) {
                completion();
            }
            self.lastPresentedController = nil;
        }];
    } else if( !self.lastPresentedController) {
        [super dismissViewControllerAnimated:flag completion:completion];
    }
}

Swift

private weak var lastPresentedController : UIViewController?

override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
    // WKWebView actions sheets workaround
    if presentedViewController != nil && lastPresentedController != presentedViewController  {
        lastPresentedController = presentedViewController;
        presentedViewController?.dismiss(animated: flag, completion: {
            completion?();
            self.lastPresentedController = nil;
        });

    } else if lastPresentedController == nil {
        super.dismiss(animated: flag, completion: completion);
    }
}
like image 133
shc Avatar answered Sep 17 '22 13:09

shc


This has to be a bug. If you override dismiss in your viewController housing the web view, it will get called when the user selects any action from the action sheet. If you put a breakpoint in your dismiss override, you can see that the presentedViewController is of type WKActionSheet which is an internal class. They're likely calling dismiss to close the action sheet, which is then trickling up to and dismissing our view controller housing the web view. You can return from within that function to prevent the closing, but then the action they chose also doesn't happen.

If you don't want to file a radar, let me know and I will.

like image 26
LordBron Avatar answered Sep 19 '22 13:09

LordBron