iOS13下获取WKWebview高度以及使用SnapKit

2,496 阅读2分钟

What 资讯类型下tableview嵌套wkwebview

开发的过程中,我们难免辉遇到tableview嵌套wkwebview的情况,基本使用tableviewHeader或者cell作为容器承载wkwebview但是如果wkwebview的高度计算不对的话,那么恭喜你,你会遇到各种各样无止境的bug

Why

  • iOS13下使用didFinish KVO获取高度的问题:

在之前我们通常使用wkwebview的代理方法或者KVO获取高度,但是这里存在的问题是:didFinish获取的高度有时是内容并未完全加载完成的高度,那么此时获取的高度肯定是不准确的;KVO那么是通过观察者的方式获取的高度,它在iOS13下首先表现的问题就是获取的高度偏大,显示的视图内容下方会留白,而且因为KVO是一个持续监听的过程,我们也无从判断什么时候什么情况下的高度是正确的;而且还有一个重要的问题是如果wkwebview内容高度计算偏小,那么你就会遇到tableview和wkwebview的手势冲突问题,别问为什么,问多了都是泪👿

How

在实际解决的过程中,我也是饱览群书(各种google),最后找到了一个解决办法(其实之前就看到了,感觉不靠谱...😭)

stackoverflow

原文:Monitor resizing of the document.body
var shouldListenToResizeNotification = false
lazy var webView:WKWebView = {
    //Javascript string
    let source = "window.onload=function () {window.webkit.messageHandlers.sizeNotification.postMessage({justLoaded:true,height: document.body.scrollHeight});};"
    let source2 = "document.body.addEventListener( 'resize', incrementCounter); function incrementCounter() {window.webkit.messageHandlers.sizeNotification.postMessage({height: document.body.scrollHeight});};"

    //UserScript object
    let script = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: true)

    let script2 = WKUserScript(source: source2, injectionTime: .atDocumentEnd, forMainFrameOnly: true)

    //Content Controller object
    let controller = WKUserContentController()

    //Add script to controller
    controller.addUserScript(script)
    controller.addUserScript(script2)

    //Add message handler reference
    controller.add(self, name: "sizeNotification")

    //Create configuration
    let configuration = WKWebViewConfiguration()
    configuration.userContentController = controller

    return WKWebView(frame: CGRect.zero, configuration: configuration)
}()

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    guard let responseDict = message.body as? [String:Any],
    let height = responseDict["height"] as? Float else {return}
    if self.webViewHeightConstraint.constant != CGFloat(height) {
        if let _ = responseDict["justLoaded"] {
            print("just loaded")
            shouldListenToResizeNotification = true
            self.webViewHeightConstraint.constant = CGFloat(height)
        }
        else if shouldListenToResizeNotification {
            print("height is \(height)")
            self.webViewHeightConstraint.constant = CGFloat(height)
        }

    }
}

使用SnapKit修改约束后立即更新视图

1.约束立即生效必须使用remakeConstraints 2.调用其父视图的setNeedsLayout()和layoutIfNeeded()

headerUIView.addSubview(myWebView)
        myWebView.snp.remakeConstraints { (make) in
            make.top.equalToSuperview().offset(ConstantsHelp.topMargin/2).priorityHigh()
            make.left.right.equalToSuperview()
            make.height.equalTo((Device().orientation == Device.Orientation.portrait ? self.webViewHeightPortrait : self.webViewHeightLandscape) ).priorityHigh()
        }
        UIView.animate(withDuration: 0.5) {
            self.headerUIView.setNeedsLayout()
            self.headerUIView.layoutIfNeeded()
        }