Can WKWebView automatically adjust its width and height based on the loaded content?

Hi Team,

We're trying to load an Image Ad inside WKWebView and we're wondering if we can adjust its width and height based on the loaded content.

Currently, we're using evaluateJavaScript("document.body.scrollHeight") query to fetch the height of the content but often times we get incorrect value from it.

We looked at the WKWebView documentation but couldn't find anything related to our use case.

Could someone from the team guide us through the next step?

Thanks

Loading an image ad inside a WKWebView and adjusting its dimensions based on the content can be tricky, especially if the document.body.scrollHeight doesn't provide accurate results. Here are some strategies you can use to dynamically size the WKWebView to fit the content, particularly images:

  1. Use Message Handlers for Dynamic Content Size

One effective way to get the dimensions of loaded images is to use JavaScript message handlers to communicate size information back to the Swift side.

Steps:

Configure a Message Handler:

Set up a message handler in your WKWebView to receive size information from JavaScript.

import WebKit

class WebViewController: UIViewController, WKNavigationDelegate { var webView: WKWebView!

override func loadView() {
    let webConfiguration = WKWebViewConfiguration()
    webView = WKWebView(frame: .zero, configuration: webConfiguration)
    webView.navigationDelegate = self
    view = webView
}

override func viewDidLoad() {
    super.viewDidLoad()

    // Add message handler
    webView.configuration.userContentController.add(self, name: "imageSizeHandler")

    // Load the image ad
    if let url = URL(string: "https://example.com/your-image-ad.html") {
        let request = URLRequest(url: url)
        webView.load(request)
    }
}

// WKNavigationDelegate method
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    // Send JavaScript to get image sizes
    let script = "var images = document.querySelectorAll('img'); var sizes = []; for (var i = 0; i < images.length; i++) { sizes.push({ width: images[i].offsetWidth, height: images[i].offsetHeight }); } window.webkit.messageHandlers.imageSizeHandler.postMessage(sizes);"
    webView.evaluateJavaScript(script)
}

}

extension WebViewController: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { if message.name == "imageSizeHandler", let sizes = message.body as? [[String: CGFloat]] { // Adjust webView's frame based on received sizes adjustWebViewSize(for: sizes) } }

func adjustWebViewSize(for sizes: [[String: CGFloat]]) {
    // Assuming you want to fit the largest image
    guard let largestImage = sizes.max(by: { $0["width"]! * $0["height"]! < $1["width"]! * $1["height"]! }) else { return }
    let newHeight = largestImage["height"]! * (webView.frame.width / largestImage["width"]!)
    webView.frame.size = CGSize(width: webView.frame.width, height: newHeight)
}

}

JavaScript to Measure Image Sizes:

The JavaScript embedded in the page calculates the dimensions of all images and sends them back via the message handler.

  1. Observe Content Size Changes

Alternatively, you can observe changes to the contentSize property of the WKWebView, although this might not always accurately reflect image sizes until they are fully loaded.

Steps:

override func viewDidLoad() { super.viewDidLoad() webView.addObserver(self, forKeyPath: "contentSize", options: .new, context: nil) }

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "contentSize" { // Adjust webView's frame based on new content size webView.frame.size = change?[ .newKey ] as? CGSize ?? webView.frame.size } }

deinit { webView.removeObserver(self, forKeyPath: "contentSize") }

Considerations

Image Loading Delays: Images might load at different times, so sizes might be reported incrementally. Consider handling partial updates or waiting for a stable size report. Complex Pages: If the page contains dynamic content or iframes, additional logic might be needed to capture all relevant images. Performance: Continuously observing content size or executing JavaScript can impact performance. Balance between accuracy and efficiency.

By using message handlers, you can get more precise control over sizing the WKWebView based on the actual content it loads, particularly images. This approach should provide more reliable results than relying solely on document.body.scrollHeight.

Can WKWebView automatically adjust its width and height based on the loaded content?
 
 
Q