iOS WKWebView detect when reaches bottom

生来就可爱ヽ(ⅴ<●) 提交于 2019-12-04 16:47:50

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"

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
}

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,

func updateButtonState(_ 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 scrollViewDidScroll(_ scrollView: UIScrollView) {
    self.updateButtonState(scrollView); 

}

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {

    isURLLoaded = true
    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500), execute:{       
        self.updateButtonState(webView.scrollView)
    });

}

Unfortunatley I can't find a callback where the scrollView is ready to evaluate, so I set a timeout of 500ms, with this modification it works as expected

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!