我正在Swift中构建一个小型iOS应用程序,并使用WKWebView。我想要实现的是在网页完全呈现时收到通知。 WKWebView的一个已知问题是它的加载通知都不起作用。
然而,这种方法似乎有效,并且想法是在Javascript中挂钩window.onload
函数并从那里通知Swift。但是,我在这里也发现了一个问题。如果我使用JQuery,在某些页面中回调似乎不会发生,因为网页已经定义了window.onload
。如果我使用纯Javascript方式,一些网页会中断,即它们根本不会加载,因为我覆盖了window.onload
。
// using JQuery, this function never get called for web pages that do window.onload =
$(window).load(function() {
window.webkit.messageHandlers.callbackHandler.postMessage(
JSON.stringify({body: "window finished loading"}));
});
// works always but breaks web pages that use window.onload
window.onload = function() {
window.webkit.messageHandlers.callbackHandler.postMessage(
JSON.stringify({body: "window finished loading"}));
};
问题是如何将此行通知附加到现有window.onload
并定义一个window.onload
(如果它不存在)?其他想法也欢迎,例如排队window.load
实施?
为了完整性,我在下面列出了使用WKWebView
本地获取此类通知的两种已知方法。
(1)获取WKNavigationDelegate
生命周期通知,但当网页尚未完全呈现时,回调通知会过早触发。
class ViewController: UIViewController {
// ...
func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
NSLog("didFinishNavigation callback received ... too early!")
}
}
(2)使用键值观察器方法(KVO),但此处再次回拨通知也会过早触发:
webView.addObserver(viewController, forKeyPath: "estimatedProgress", options: .New, context: nil)
//
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<()>) {
guard let webView = object as? WKWebView else {return}
guard let change = change else {return}
guard let keyPath = keyPath else {return}
switch keyPath {
case "estimatedProgress":
if ((1.0 - webView.estimatedProgress) < 1e-10) {
NSLog("'estimatedProgress' callback received ... too early!")
}
break
default: break
}
}
“装载”KVO也是同样的问题。
更新:除了接受的答案之外,对此另一个问题running-jquery-after-all-other-js-has-executed的排名最高的答案也通过轮询DOM以获得特定更改来解决此问题,例如:除了加载页面之外,当所有Javascript都已完成时。
答案 0 :(得分:3)
而不是load事件,您可以收听DOMContentLoaded。
您还可以通过执行以下操作在执行堆栈的末尾执行回调:
window.setTimeout(callback, 0);
此外,您可以尝试在回调中调用removeEventListener
。
例如:
if (window.addEventListener) {
var documentIsReady = function() {
window.removeEventListener("load", documentIsReady);
if (typeof window.isMyiOSAppAlreadyNotified === 'undefined') {
window.webkit.messageHandlers.callbackHandler.postMessage(JSON.stringify({body: "window onload"}));
}
window.isMyiOSAppAlreadyNotified = true;
};
window.addEventListener("load", function() { window.setTimeout(documentIsReady, 0); });
}
为避免使用isMyiOSAppAlreadyNotified
等变量污染全局(窗口)范围,您可以应用module pattern。
答案 1 :(得分:1)
我找到了一种有效的方法。然而,尽管我徒劳地试图避免重复通知,但仍然会收到一些网页的通知,但还没有找到解决方法......
if (window.addEventListener) {
window.addEventListener("load",
function() {
// try to make sure it is called only once ...
if (typeof window.isMyiOSAppAlreadyNotified === 'undefined') {
// notify my iOS App that the page has finished loading
window.webkit.messageHandlers.callbackHandler.postMessage(
JSON.stringify({body: "window onload"}));
}
window.isMyiOSAppAlreadyNotified = true;
}
);
}