My app uses WKWebView to present html string. WkWebView is embedded in a scrollView because i have several other elements (e.g. buttons, tableViews). Because of that i need to calculate the size of WkWebvView content and for that i use evaluateJavaScript(“document.body.scrollHeight”, completionHandler: method. Whenever i have embedded tweet in a html string, content height is wrongly calculated and my webview cuts a piece of its height.
Part of html string which has tweet embedded looks like this:
<blockquote class=\"twitter-tweet\" data-lang=\"en\"><p><a
href=\"https://twitter.com/hashtag/SYRIA?src=hash\">#SYRIA</a> Admiral Essen frigate launched <a href=\"https://twitter.com/hashtag/Kalibr?src=hash\">#Kalibr</a> cruise missiles against ISIS objects near Deir ez-Zor <a href=\"https://BLABLA*\">pic.twitter.com/azHAIii07g</a></p><p>— Минобороны России (@mod_russia) <a href=\"https://twitter.com/mod_russia/status/905007557554700288\">September 5, 2017</a></p></blockquote><p><script src=\"https://platform.twitter.com/widgets.js\"></script>
(BLABLA* is actually t.co/azHAIii07g which i had to replace because of stackoverflow rules)
Is there something missing in this twitter code, or should i set something in webview methods?
I tried implementing solution from this post: iOS Calculate correct WKWebview height for html string with embeded tweet but there was no success or at least i could figure out the answer.
Any advice is appreciated??
UPDATE: I hacked a little bit and found horrible workaround, but it is a good lead for where the problem lies. This is the full function which i use for calculating height:
`func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.webViewx.evaluateJavaScript("document.readyState", completionHandler: {
[weak self] (complete, error) in
if complete != nil { self?.webViewx.evaluateJavaScript("document.body.offsetHeight", completionHandler: {
[weak self] (height, error) in
})
}`
If i put:
`DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1, execute: {
self?.webViewx.evaluateJavaScript("document.body.offsetHeight", completionHandler: {
[weak self] (height, error) in
print("HEIGHT AFTER 1 sec", height)
})
})`
I get correct height after 1 second. Now, what would be the way to get that height update after correct time.
Also, one side note, if i have embedded facebook or instagram post, everything works as it should and webview height gets properly calculated.
With the help several posts on stackoverflow (especially: Twitter Uncaught TypeError: undefined is not a function), and my colleague who is a web developer, I managed to find a solution to this issue. Basically, the idea is to add a script to the webview configuration if there is an embedded tweet in a html string which webview needs to load. After that we should implement userContentController didReceive message function and in it we should evaluate the height of a webview.
Here is the code which should go before webview.loadHtmlString method:
if htmlString.contains("platform.twitter.com/widgets.js") {
let twitterJS: String = "window.twttr = (function(d, s, id) {var js, fjs = d.getElementsByTagName(s)[0],t = window.twttr || {};if (d.getElementById(id)) return t;js = d.createElement(s);js.id = id;js.src = \"https://platform.twitter.com/widgets.js\";fjs.parentNode.insertBefore(js, fjs);t._e = [];t.ready = function(f) {t._e.push(f);};return t;}(document, \"script\", \"twitter-wjs\"));twttr.ready(function (twttr) {twttr.events.bind('loaded', function (event) {window.webkit.messageHandlers.callbackHandler.postMessage('TWEET');});});"
let userScript = WKUserScript(source: twitterJS, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
webViewx.configuration.userContentController.removeAllUserScripts()
webViewx.configuration.userContentController.addUserScript(userScript)
webViewx.configuration.userContentController.add(self, name: "callbackHandler")
}
First there is a check if a html string has a substring which is an embedded tweet. Script assigned to let twitterJS is something we got from twitter web page. Last line is important in a sense that it gives the name to our script.
Actual height evaluation is done like this:
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name == "callbackHandler" {
print(message.body)
webViewx.evaluateJavaScript("document.body.offsetHeight", completionHandler: { (height, error) in
if let error = error {
print(error.localizedDescription)
return
}
if let height = height as? CGFloat{
if height > self.kontejnerHeight.constant {
self.kontejnerHeight.constant = height
}
}
})
}
}
Hopefully this will help the others as well.