实际显示视图时的 WKWebView 通知

Posted

技术标签:

【中文标题】实际显示视图时的 WKWebView 通知【英文标题】:WKWebView notification when view is actually shown 【发布时间】:2015-09-18 19:55:35 【问题描述】:

我想截取WKWebView 的屏幕截图。在 Web 视图完成加载后,我立即调用方法 drawViewHierarchyInRect(屏幕更新设置为 true)——为此,我观察了 Web 视图的 loading 属性。但是,我注意到,当 web 视图通知我加载完成时,它实际上并没有在屏幕上显示自己。这就是为什么屏幕截图总是只有白色的。当我在加载后 0.5 秒截取屏幕截图时(显然太长了),屏幕截图显示了所需的结果。我的问题是:我不知道网络视图何时实际显示在屏幕上,我可以将延迟设置为 0.05,但我不能 100% 确定它每次都能正常工作。因此,我的问题是:如何在实际显示 Web 视图并准备好截屏时通知我。

提前致谢!

顺便说一句:我正在使用 Swift 2.0 和 ios 9 以及 Xcode 7(正式版本)

【问题讨论】:

【参考方案1】:

因为我刚刚在这方面工作,所以请告诉我我尝试了什么以及我是如何解决的。我尝试了几件事,首先,一个 WKUserScript

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

在“状态”命名空间上。这个理论上是等待所有内容加载完毕,然后调用WKScriptMessageHandler

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

通过以下方式注册时

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

我以为一切都准备好了。有时有效,但并非总是如此。

和你一样,我也尝试过添加 WKNavigationDelegate 并使用

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

同样,这没有用。另外,为estimatedProgress 值添加一个观察者

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

然后尝试在

内制作屏幕截图
override func observeValueForKeyPath(keyPath: String?, ofObject: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>)  

没用。

但是,有效的是定期检查 webView 的内容大小。我在由 WKUserScript 调用的 userContentController 函数中执行了此操作 - 但您也可以在 didFinishNavigation 函数中执行此操作,然后只需更改要再次调用的函数。代码如下:

let _contentSize = webview.scrollView.contentSize;

if (_contentSize.height == 0)          
  dispatch_after(createDispatchTime(0.01), dispatch_get_main_queue()) 
                self.userContentController(userContentController, didReceiveScriptMessage: message)
  
  return

这实质上是检查 contentSize 高度属性,如果它为零,它将每 0.01 秒再次调用该方法,直到设置该属性为止。这是我用于创建屏幕截图的函数

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

我已经在设备和模拟器上对其进行了测试,并且可以正常工作。

编辑

这里是创建调度时间的函数

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

编辑#2

我创建了一个要点,它展示了我认为更好的方式 https://gist.github.com/christopherhelf/1640454bcace39a87c93#file-wkwebviewplus-swift

我替换了 webviews UIScrollview 的 setContentSize 函数,并通知父类发生了变化。

【讨论】:

我尝试了你的 swizzle 方法实现。甚至在 webView 实际呈现非常快的网页(如 Wikipedia)之前,它也会被触发 您还可以观察 contentSize 属性,而不是触发延迟函数或覆盖 setContentSize。这似乎适用于检测大小变化,但它似乎也可以在常规动画周期中触发,尽管没有变化。 这绝对是最好的答案之一。 iOS WebView didFinishLoad 在加载主要内容时调用,但 iOS WebKit 没有任何可靠的 javascript 控件。问题就在这里。主要内容已加载,但一些 javascript 仍在工作(加载、重定向等)并且 WebKit 无法跟踪它。解决方案是控制 javascript Web 内容并在加载所有数据时调用脚本消息。我正在使用 [github.com/marcuswestin/WebViewJavascriptBridge] (WebViewJavascriptBridge)。 现在有一个 isLoading() 方法返回一个布尔值,您可以检查它而不是检查 contentsize 高度。 @peacer212 可以找到 avplayer 在 WKWebView 中显示的时间,如果有任何解决方案,请参见 ***.com/questions/55377677/…

以上是关于实际显示视图时的 WKWebView 通知的主要内容,如果未能解决你的问题,请参考以下文章

显示带有评论的文章,​​例如 Medium 应用程序

使WKWebView在后台保持不变,同时显示另一个视图

从 WKWebView 获取所有 cookie

触发本地通知时的自定义视图

Javascript:处理通知的Webview

无法显示添加到 WKWebView 的子视图