cmii
cmii

Reputation: 3626

How to preload WKWebView before pushing its viewcontroller?

I ask this question because after some research, I haven't found satisfactory answers on the Web.

My need is simple, I have a UITableViewController, when I click on a cell, I need to display a loader (while loading the WKWebViewContent), THEN push the next UIViewController, with the WKWebView already loaded inside.

I tried this :

class TableViewController: UITableViewController, WKNavigationDelegate {

   var webviewToLoad:WKWebView!

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        let htmlString = "some html content"


        webviewToLoad = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
        webviewToLoad.navigationDelegate = self
        webviewToLoad.loadHTMLString(htmlString, baseURL: Bundle.main.resourceURL)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        if segue.identifier == "postview"{
            let destinationController = segue.destination as! ViewController
            destinationController.webView= webviewToLoad
        }
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        print("Loaded!")
        self.performSegue(withIdentifier: "postview", sender: self)
    }

}

class ViewController: UIViewController, WKUIDelegate {

    var webView: WKWebView!

    override func loadView() {

        let webConfiguration = WKWebViewConfiguration()
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.uiDelegate = self
        self.view = webView
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

didFinish is called, but the final WKWebView is blank.

Maybe it isn't the right way, any ideas?

Upvotes: 4

Views: 5354

Answers (2)

cmii
cmii

Reputation: 3626

As @matt's answer was incomplete, here my solution to wait the webview loading before pushing the next view controller:

class TableViewController: UITableViewController, WKNavigationDelegate, WKUIDelegate {

    var webviewToLoad:WKWebView!
    var destinationController:ViewController!
    var alert:UIAlertController!

    override func viewDidLoad() {
        super.viewDidLoad()       
    }

    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        alert = UIAlertController(title: "Alert", message: "Loading", preferredStyle: .alert)

        destinationController = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: "ViewController") as? ViewController

        destinationController!.loadViewIfNeeded()
        let webView:WKWebView = destinationController!.webView
        webView.navigationDelegate = self
        webView.uiDelegate = self

        self.present(alert, animated: true, completion: nil)

        let htmlString = "my html code"
        webView.loadHTMLString(htmlString, baseURL: Bundle.main.resourceURL)
    }


    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {

        alert.dismiss(animated: false, completion: nil)
        self.navigationController?.pushViewController(destinationController!, animated: true)
    }

}

And the UIViewController with the webview inside:

class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {

    var webView: WKWebView!
    @IBOutlet var viewForWebview: UIView!
    @IBOutlet var heightWebviewConstraint: NSLayoutConstraint!

    override func viewDidLoad() {
        super.viewDidLoad()

    }

     override func loadView() {

        super.loadView()

        webView = WKWebView()

        if (webView != nil) {
            webView!.frame = viewForWebview.frame

            webView.translatesAutoresizingMaskIntoConstraints = false

            viewForWebview.addSubview(webView!)

            NSLayoutConstraint.activate([
               webView.topAnchor.constraint(equalTo: viewForWebview.topAnchor),
                webView.bottomAnchor.constraint(equalTo: viewForWebview.bottomAnchor),
                webView.leadingAnchor.constraint(equalTo: viewForWebview.leadingAnchor),
                webView.trailingAnchor.constraint(equalTo: viewForWebview.trailingAnchor)
                ])
        }

    }


    override func viewDidLayoutSubviews() {

        super.viewDidLayoutSubviews()

        heightWebviewConstraint.constant = self.webView.scrollView.contentSize.height
        self.view.setNeedsLayout()
        self.view.setNeedsUpdateConstraints()


    }    
}

This code displays an UIAlertController during the webview loading, then dismiss the alert controller, and pushing the next controller, with the webview already loaded.

Upvotes: 3

matt
matt

Reputation: 534893

didFinish is called, but the final WKWebView is blank

Because you never did anything to make it not blank. Your code says:

var webviewToLoad:WKWebView!
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    webviewToLoad = WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
    webviewToLoad.navigationDelegate = self
    webviewToLoad.loadHTMLString(htmlString, baseURL: Bundle.main.resourceURL)
}

So everything you do is about webviewToLoad. But webviewToLoad is not the same webView that appears in the ViewController after the segue:

class ViewController: UIViewController, WKUIDelegate {
    var webView: WKWebView!
}

That is the web view you want to give content to. You are not doing that at all; none of your code touches webView to give it content.

I think the heart of your confusion is here:

if segue.identifier == "postview"{
    let destinationController = segue.destination as! ViewController
    destinationController.webView = webviewToLoad
}

You cannot just substitute one web view for another and expect things to magically work; loadView will still cause the original blank web view to be your view and to appear in the interface.

Instead, what you want is this architecture:

if segue.identifier == "postview"{
    let destinationController = segue.destination as! ViewController
    // make `loadView` run
    destinationController.loadViewIfNeeded() 
    let webView = destinationController.webView
    // now load _this_ `webView` with content!
    let htmlString = "some html content"
    webView.loadHTMLString(htmlString, baseURL: Bundle.main.resourceURL)
}

Upvotes: 2

Related Questions