I have a migration issue from UIViewView to WKWebView, detecting the Scroll View reached bottom when using WKWebView. Prior to WKWebView I used the UIScrollViewDelegate detecting wether the User had seen all of the content by scrolling till the end of the WebView. If he did, the "confirm" button was enabled. iPhone - knowing if a UIScrollView reached the top or bottom
Now with WKWebView this doesn't work anymore. I guess the reason is, when using a WKWebView and load a html string, it scales the view down for full visiblity of the content. So I had to set the viewport by appending it to the html string. This displays the content in the same way, the UIWebView did, providing the html string, without setting a viewport.
But now the UIScrollViewDelegate on load always tells that the bottom already reached. I guess, that the WKWebView loads the html, scales it at full visiblity, the scrollViewDelegate recognizes, that the content was fully visible, after that the viewport comes in and scales the page up, so a vertical scroll is needed to display the full content. But at this time, my "confirm" button is already enabled.
Code Snippet
override func scrollViewDidScroll(_ scrollView: UIScrollView){
let scrollViewHeight = scrollView.frame.size.height;
let scrollContentSizeHeight = scrollView.contentSize.height;
let scrollOffset = scrollView.contentOffset.y;
if (scrollOffset + scrollViewHeight == scrollContentSizeHeight)
{
self.confirmButton.isEnabled = true;
}
}
With WKWebView, the scrollContentSizeHeight always is the same as scrollViewHeight on load, but after the scrollViewDidScroll delegate function invokes mulitple times (without scrolling) the scrollContentSizeHeight is larger than the scrollViewHeight at real size.
But now the UIScrollViewDelegate on load always tells that the bottom already reached.
The issue in the particular case is UIScrollView
delegate is getting called before the WKWebView
is loaded completely.
Take one private instance variable to check if the URL
is loaded completely or not.
var isURLLoaded = false
Confirm WKWebView
delegates to your viewController.
webView.scrollView.delegate = self
webView.navigationDelegate = self
And override these delegate methods:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if isURLLoaded {
if scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height) {
confirmButton.isEnabled = true
} else if scrollView.contentOffset.y < scrollView.contentSize.height {
confirmButton.isEnabled = false
}
}
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
isURLLoaded = true
}
I wrote scrollview extension for Wkwebview using to read javascript code.through this you can use to check webview reaches to end of page or not. Before use, Set webview scroll delegate.
extension BaseWebViewController: UIScrollViewDelegate {
func scrollViewDidEndDragging(_ scrollView: UIScrollView,
willDecelerate decelerate: Bool) {
webView.evaluateJavaScript("document.readyState", completionHandler: { (complete, error) in
if complete != nil {
self.webView.evaluateJavaScript("document.body.scrollHeight", completionHandler: { (height, error) in
let bodyScrollHeight = height as! CGFloat
var bodyoffsetheight: CGFloat = 0
var htmloffsetheight: CGFloat = 0
var htmlclientheight: CGFloat = 0
var htmlscrollheight: CGFloat = 0
var wininnerheight: CGFloat = 0
var winpageoffset: CGFloat = 0
var winheight: CGFloat = 0
//body.offsetHeight
self.webView.evaluateJavaScript("document.body.offsetHeight", completionHandler: { (offsetHeight, error) in
bodyoffsetheight = offsetHeight as! CGFloat
self.webView.evaluateJavaScript("document.documentElement.offsetHeight", completionHandler: { (offsetHeight, error) in
htmloffsetheight = offsetHeight as! CGFloat
self.webView.evaluateJavaScript("document.documentElement.clientHeight", completionHandler: { (clientHeight, error) in
htmlclientheight = clientHeight as! CGFloat
self.webView.evaluateJavaScript("document.documentElement.scrollHeight", completionHandler: { (scrollHeight, error) in
htmlscrollheight = scrollHeight as! CGFloat
self.webView.evaluateJavaScript("window.innerHeight", completionHandler: { (winHeight, error) in
if error != nil {
wininnerheight = -1
} else {
wininnerheight = winHeight as! CGFloat
}
self.webView.evaluateJavaScript("window.pageYOffset", completionHandler: { (winpageOffset, error) in
winpageoffset = winpageOffset as! CGFloat
let docHeight = max(bodyScrollHeight, bodyoffsetheight, htmlclientheight, htmlscrollheight,htmloffsetheight)
winheight = wininnerheight >= 0 ? wininnerheight : htmloffsetheight
let winBottom = winheight + winpageoffset
if (winBottom >= docHeight) {
print("end scrolling")
}
})
})
})
})
})
})
})
}
})
}
}
After scroll reaches to end you will see in console "end scrolling"
Create a ScrollView inside the class, WKWebView has a scrollview embedded inside, make the scrollview you created relative with the embedded scrollview and implemente the method :
func scrollViewDidScroll(_ scrollView: UIScrollView){
if (scrollView.contentOffset.y + 1) >= (scrollView.contentSize.height - scrollView.frame.size.height) {
//bottom reached
your code here
}
}
and then, make your class inherit from UIScrollViewDelegate :
class : UIViewController,
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With