WKWebView notification when view is actually shown

后端 未结 1 1200
忘掉有多难
忘掉有多难 2020-12-31 08:34

I want to take a screenshot of a WKWebView. I call the method drawViewHierarchyInRect (with screen updates set to true) right after th

相关标签:
1条回答
  • 2020-12-31 08:39

    as I've just worked on this, let me know what I tried and how I figured it out. I tried a couple of things, first, a WKUserScript

    let webViewUserScript = WKUserScript(
            source: "window.onload = function() { webkit.messageHandlers.status.postMessage(\(wkwebView.hashValue)) }",
            injectionTime: WKUserScriptInjectionTime.AtDocumentEnd,
            forMainFrameOnly: true
        )
    

    on the "status" namespace. This theoretically waits for all contents to be loaded, and then calls the WKScriptMessageHandler

    func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage){ }
    

    when registered via

    wkwebView.configuration.userContentController.addScriptMessageHandler(self, name: "status")
    

    where I thought everything would be ready. Worked sometimes, but not always.

    As you, I've also tried adding a WKNavigationDelegate and using

    func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) { }
    

    Again, this didn't work. Also, adding an observer for the estimatedProgress value

    wkwebView.addObserver(self, forKeyPath: "estimatedProgress", options: .New, context: nil)
    

    and then trying to make the screenshot within

    override func observeValueForKeyPath(keyPath: String?, ofObject: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) { }
    

    didn't work.

    However, what does work is to check upon the webView's content size regularly. I've did this within the userContentController function, which is called by the WKUserScript - but you can also do this within the didFinishNavigation function, and then simply change the function which is to be called again. Here the code:

    let _contentSize = webview.scrollView.contentSize;
    
    if (_contentSize.height == 0){          
      dispatch_after(createDispatchTime(0.01), dispatch_get_main_queue()) {
                    self.userContentController(userContentController, didReceiveScriptMessage: message)
      }
      return
    }
    

    This essentially checks on the contentSize height property and if it's zero, it will call the method again every 0.01 seconds until the property is set. Here are the functions I used for creating the screenshot

    var view : UIView!
    
    if self.useSnapShot {
       // Use the snapshot function
       view = webView.snapshotViewAfterScreenUpdates(true)
    
    } else {
       // Draw the view as UIImage
       UIGraphicsBeginImageContextWithOptions(webView.frame.size, false, 0.0)
       webView.drawViewHierarchyInRect(webView.frame, afterScreenUpdates: true)
       let snapshot = UIGraphicsGetImageFromCurrentImageContext()
       view = UIImageView(image: snapshot)
       view.frame = webView.frame
    }
    

    I've tested it on both device and simulator and it works.

    Edit

    Here the function for creating the dispatch time

    private func createDispatchTime(seconds: Double) -> dispatch_time_t{
        let delay = seconds * Double(NSEC_PER_SEC)
        return dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
    }
    

    Edit #2

    I created a gist which shows an even better way I believe https://gist.github.com/christopherhelf/1640454bcace39a87c93#file-wkwebviewplus-swift

    I replaced the setContentSize function of the webviews UIScrollview and notify the parent class that a change has been made.

    0 讨论(0)
提交回复
热议问题