Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 988
cmii Avatar asked Oct 23 '25 01:10

cmii


2 Answers

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.

like image 168
cmii Avatar answered Oct 24 '25 14:10

cmii


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)
}
like image 36
matt Avatar answered Oct 24 '25 13:10

matt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!