Determining the content size of a WKWebView
In one of my recent projects, I had to use a web view to display an HTML string with local CSS and custom font. The web view had to be embedded inside a scroll view along with a couple of other views. It also had to have a height adjusted to the content it was displaying. My goal was to create something like this:
I had already done this in one of my previous projects so I figured I’d reuse the same code.
The problem was that the code was using a UIWebView
instead of the newer WKWebView
. Since iOS 8, UIWebView
was replaced with a much more powerful and well structured WKWebView
, and since iOS 12 UIWebView
was officially deprecated. That meant that I had to reimplement the feature.
I’ve put all the code in an example project on GitHub so you can follow along.
Loading HTML string
For loading HTML strings into the web view, WKWebView
has loadHTMLString(_:baseURL:)
method. The HTML string that I was getting from backend did not have <HTML>
, <HEAD>
and <BODY>
tags so I had to manually add them. The method for loading the HTML looked like this:
Important notes:
- The
<HEAD>
tag has to haveviewport
metadata which is important for the correct calculation of the content height of the web view. - The
baseURL
parameter inloadHTMLString
has to be the main bundle URL because we will be loading our local custom font.
Loading local CSS
Loading local CSS file into HTML was pretty easy in UIWebView
. You only had to add <link rel='stylesheet' href='/path/to/css' type='text/css'>
in the <HEAD>
tag.
In WKWebView
the process is a bit more complicated because it requires CSS to be injected after HTML loads. We can do that by utilizing the webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)
method of the WKNavigationDelegate
:
First, we load the contents of the style.css
file into a string variable. After that, we use JavaScript to inject a <style>
tag into web view’s HTML.
The problem with this approach is that the web view loads HTML, displays it and after that applies CSS. That transition is visible by the user which is not good.
Fortunately, there is a better approach. We can use WKUserScript
that represents a script that can be injected into a webpage, and WKUserContentController
that allows us to inject that script into the web view. These objects will be used inside a WKWebViewConfiguration
during WKWebView
initialization:
Using this approach, CSS will be loaded just after HTML loads and there won’t be a transition that can be noticed by the user.
Adding custom font in CSS
To add custom fonts in Xcode, I suggest you follow Apple’s tutorial. After adding the font, we need to load it into the web view using CSS. To use custom fonts in CSS we need to use the @font-face
CSS rule:
This way, we have defined our custom font and it can now be used in CSS:
Calculating web view’s content height
After loading HTML and CSS in the web view we have to calculate it’s height. To calculate the height we’ll use JavaScript to find the scrollHeight
of the document in the web view. We’ll do that in the delegate method to make sure that the web view finished loading before calculation:
Conclusion
In this blog post, we’ve gone through one use case of WKWebView
and analyzed the problems that can occur. We can see that the new API gives us much more flexibility and stability compared to the deprecated UIWebView
.