user2875404
user2875404

Reputation: 3218

Load ViewController's WebView before showing

I have an app with a ViewController that contains a WebView. Because the website the WebView is showing is quite large, I'd like to load the ViewController while the app is launching so as soon as the user opens the ViewController with the WebView it will show the already loaded page.

I've tried overriding viewDidLoad(), loadView(), a convenience init() etc. but nothing I have put there seems to get executed before actually showing the ViewController.

 let storyboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
 webViewVC = storyboard.instantiateViewController(withIdentifier: "WebViewController")

 if let c = webViewVC.childViewControllers[0] as? WebViewController {

     c.dDelegate = self

     webViewVC.modalPresentationStyle = .popover
     let popover = webViewVC.popoverPresentationController!
     popover.delegate = self
     popover.permittedArrowDirections = .up
     webViewVC.loadView()
     present(webViewVC, animated: true, completion: nil) //if I don't add this line, the ViewController doesn't seem to get created before showing
 }

So what can I call before present() in order to execute some of the ViewController's code? I need to load stuff like the following:

let wwConfiguration = WKWebViewConfiguration()

webView = WKWebView(frame: CGRect(x: 0, y: 0, width: self.view.layer.frame.width, height: self.view.layer.frame.height), configuration: wwConfiguration)
webView.navigationDelegate = self
webView.uiDelegate = self
let url = URL(string: . . .

Upvotes: 2

Views: 3352

Answers (3)

Deepika
Deepika

Reputation: 468

Try using webViewVC.loadViewIfNeeded() instead of webViewVC.loadView()

Upvotes: -2

Athul George
Athul George

Reputation: 164

Okay lets try to understand what the OP is asking for. He has got a viewController which loads a webpage and as per him its taking too long to load and he doesnt wants to make the user wait that long. Also as per his question specifying about launching time I presume the view controller is the first view controller he will be displaying after app launch.

Case 1: The webViewVC is the first VC after launch

Here, the maximum "prefetching" time that we can obtain is the time we get when the method application:didFinishLaunchingWithOptions: is called. Assuming that there is not much processor heavy process executing here, there wont be any time for prefetching. So the only option is to show a screen which should be similar in UI as the launchScreen so that the user MAY think it as the application still loading. (Personally what i would do is to make the launch screen as a screen which just shows the app theme colour and animate my first screen as a launch screen using the app icon or so; gives a better professional approach and also enough time for me to get things rolling)

Case 2: The webViewVC is not the first VC

Here, the app will have another VC displayed to the user before moving on to the webViewVC so that gives us some time required to prefetch the data. In this case I would start fetching for the data while the viewDidLoad of the previous VC. The webViewVC will obviously be called after some actions and you get time till that action is done (probably by the user).

Loading the VC by using a DUMMY launch screen

    let webViewVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "WebViewController") as! WebViewController
    let wwConfiguration = WKWebViewConfiguration()
    let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: view.bounds.height), configuration: wwConfiguration)
    webView.navigationDelegate = webViewVC
    webView.uiDelegate = webViewVC
    let url = URL(string:"https://www.codemag.com/article/1405051/Understanding-and-Using-iBeacons")
    webView.load(URLRequest(url: url!))
    webViewVC.webView = webView
    DispatchQueue.main.asyncAfter(deadline: .now() + 10.0) {
        webViewVC.modalPresentationStyle = .popover
        let popover = webViewVC.popoverPresentationController!
        //popover.delegate = webViewVC
        popover.permittedArrowDirections = .up
        webViewVC.loadView()
        self.present(webViewVC, animated: true, completion: nil)
        webViewVC.showWebView()
    }

This code instantiates the webviewVC and start loading the data but is shown only after 10 sec prefetch time. The showWebView() method is simply

func showWebView() {
    view.addSubview(webView)
}
  1. You can use the answer by @AshleyMills to load the webView once thhe data fetch is complete or
  2. You can load the webView after a delay of some time (10 sec in my example) or
  3. You can load the page as an action (for eg on a button click) by changing all the code inside the DispatchQueue.main.asyncAfter() to the button action. Here you may use the @AshleyMills code to enable the button once the lead is complete.

Cheers!

Upvotes: 1

Ashley Mills
Ashley Mills

Reputation: 53111

It's not entirely clear from your question, but I'm assuming that you're presenting your web view controller from another view controller from, let's say, a button press. Something like this…

enter image description here

What you need to do is to handle this asynchronously.

  • Instantiate your web view controller
  • Start loading its web view
  • Wait for that to complete
  • Present the web view controller

Example…

WebViewController

class WebViewController: UIViewController {

    @IBOutlet private weak var webView: WKWebView!

    var didLoadWebView: (() -> Void)!

    func loadWebView(completion: @escaping () -> Void) {
        loadViewIfNeeded()
        didLoadWebView = completion // Save this closure to be called when loaded
        webView.navigationDelegate = self
        webView.load(URLRequest(url: URL(string: "http://apple.com")!))
    }
}

extension WebViewController: WKNavigationDelegate {
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        didLoadWebView() // Call back to the presenting view controller
    }
}

Presenting ViewController

class ViewController: UIViewController {

    @IBAction func showWebView(_ sender: UIButton) {

        let wvc = storyboard!.instantiateViewController(withIdentifier: "WebViewController") as! WebViewController
        wvc.modalPresentationStyle = .popover
        wvc.popoverPresentationController?.sourceView = sender
        wvc.popoverPresentationController?.sourceRect = sender.bounds
        wvc.loadWebView {
            self.present(wvc, animated: true, completion: nil)
        }
    }
}

Upvotes: 5

Related Questions