Saturday, March 3, 2018

Preload multiple local WebViews

Leave a Comment

Because I could just find some outdated information / not working solutions to my problem I decided to ask this kind of question again. (See outdated / wrong solutions:)


My app is separated in one native part and one HTML part. The HTML is saved as a local file (index.html) and should be load into the myWebView view.

@IBOutlet weak var myWebView: UIWebView! func loadWebview() {     let url = Bundle.main.url(forResource: "index", withExtension: "html")     let request = URLRequest(url: url!)     myWebView.loadRequest(request)     myWebView.scrollView.isScrollEnabled = false     myWebView.allowsLinkPreview = false     myWebView.delegate = self } 

Because my DOM tree is very large, switching from the native part to the web part (on button click) takes quite a long time at -least for the first time switching- because afterward, I'm sure the webView-request gets cached.

To my question: How can I preload the WebView on app init to avoid the white screen (maybe 0.5s - 1s duration) when switching from the native to the Web part?

EDIT:

The WKWebView is displaying the scrollbar while the UIWebView was not!

Using (like with UIWebView) this styles:

::-webkit-scrollbar {      display: none; } 

is not working and adding these lines:

webview.scrollView.showsHorizontalScrollIndicator = false webview.scrollView.showsVerticalScrollIndicator = false 

is also not working at all.

1 Answers

Answers 1

Firstly, you should switch to WKWebView, UIVewView is no longer recommended to be used by Apple.

Secondly, you can create a pool of web views that get created and asked to load when the app starts. This way by the time the user switches to the web interface the web view might've got a chance to fully load.

For this you can use a class like this:

/// Keeps a cache of webviews and starts loading them the first time they are queried class WebViewPreloader {     var webviews = [URL: WKWebView]()       /// Registers a web view for preloading. If an webview for that URL already     /// exists, the web view reloads the request     ///     /// - Parameter url: the URL to preload     func preload(url: URL) {         webview(for: url).load(URLRequest(url: url))     }      /// Creates or returns an already cached webview for the given URL.     /// If the webview doesn't exist, it gets created and asked to load the URL     ///     /// - Parameter url: the URL to prefecth     /// - Returns: a new or existing web view     func webview(for url: URL) -> WKWebView {         if let cachedWebView = webviews[url] { return cachedWebView }          let webview = WKWebView(frame: .zero)         webview.load(URLRequest(url: url))         webviews[url] = webview         return webview     } } 

and ask it to preload the url sometimes during the app startup:

// extension added for convenience, as we'll use the index url in at least // two places extension Bundle {     var indexURL: URL { return self.url(forResource: "index", withExtension: "html")! } }  webviewPreloader.preload(url: Bundle.main.indexURL) 

Thirdly, you might need to use a container view instead of the actual web view, in your controller:

@IBOutlet weak var webviewContainer: UIView! 

What remains is to add the preloaded web view to the container when needed:

func loadWebview() {     // retrieve the preloaded web view, add it to the container     let webview = webviewPreloader.webview(for: Bundle.main.indexURL)     webview.frame = webviewContainer.bounds     webview.translatesAutoresizingMaskIntoConstraints = true     webview.autoresizingMask = [.flexibleWidth, .flexibleHeight]     webviewContainer.addSubview(webview) } 

And not lastly, be aware that keeping alive instances of web views, might carry performance penalties - memory and CPU-wise.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment