在WKWebView中调用本地代码的JavaScript函数

在iOS 8中使用WKWebView,我如何从本地运行JavaScript函数,或者从本地运行到JavaScript? 似乎没有类似于UIWebView的stringByEvaluatingJavaScriptFromString:

(我可以在configuration.userContentController对象上使用- addScriptMessageHandler:name:来允许从JS到本地的通信,但我正在寻找相反的方向。)

(我在这里提出这个问题后,我很快提出了一个雷达。)

一个新的方法刚刚添加了几天前(感谢jcesarmobile指出):

添加-[WKWebView evaluateJavaScript:completionHandler:]
http://trac.webkit.org/changeset/169765

该方法在iOS 8 beta 3及更高版本中可用。 这是新的方法签名:

 /* @abstract Evaluates the given JavaScript string. @param javaScriptString The JavaScript string to evaluate. @param completionHandler A block to invoke when script evaluation completes or fails. @discussion The completionHandler is passed the result of the script evaluation or an error. */ - (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler; 

文档可以在这里find: https : //developer.apple.com/documentation/webkit/wkwebview/1415017-evaluatejavascript 。

细节

xCode 9.1,Swift 4

描述

该脚本插入到将显示在WKWebView中的页面。 这个脚本将返回页面的URL(但你可以写另一个JavaScript代码)。 这意味着脚本事件是在网页上生成的,但是会在我们的函数中处理:

 func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {...} 

 extension WKUserScript { class func getUrlScript(scriptName: String) -> String { return "webkit.messageHandlers.\(scriptName).postMessage(document.URL)" } } extension WKWebViewConfiguration { func addScript(script: String, scriptHandlerName:String, scriptMessageHandler: WKScriptMessageHandler, injectionTime:WKUserScriptInjectionTime) { let userScript = WKUserScript(source: script, injectionTime: injectionTime, forMainFrameOnly: false) userContentController.addUserScript(userScript) userContentController.add(scriptMessageHandler, name: scriptHandlerName) } } 

用法

初始化WKWebView

  let config = WKWebViewConfiguration() config.addScript(script: WKUserScript.getUrlScript(scriptName: getUrlAtDocumentStartScript), scriptHandlerName:getUrlAtDocumentStartScript, scriptMessageHandler: self, injectionTime: .atDocumentStart) config.addScript(script: WKUserScript.getUrlScript(scriptName: getUrlAtDocumentEndScript), scriptHandlerName:getUrlAtDocumentEndScript, scriptMessageHandler: self, injectionTime: .atDocumentEnd) webView = WKWebView(frame: UIScreen.main.bounds, configuration: config) webView.navigationDelegate = self 

捕捉事件

 extension ViewController: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { switch message.name { case getUrlAtDocumentStartScript: print("start: \(message.body)") case getUrlAtDocumentEndScript: print("end: \(message.body)") default: break; } } } 

完整的代码示例

 import UIKit import WebKit class ViewController: UIViewController, WKNavigationDelegate { var webView = WKWebView() let getUrlAtDocumentStartScript = "GetUrlAtDocumentStart" let getUrlAtDocumentEndScript = "GetUrlAtDocumentEnd" override func viewDidLoad() { super.viewDidLoad() let config = WKWebViewConfiguration() config.addScript(script: WKUserScript.getUrlScript(scriptName: getUrlAtDocumentStartScript), scriptHandlerName:getUrlAtDocumentStartScript, scriptMessageHandler: self, injectionTime: .atDocumentStart) config.addScript(script: WKUserScript.getUrlScript(scriptName: getUrlAtDocumentEndScript), scriptHandlerName:getUrlAtDocumentEndScript, scriptMessageHandler: self, injectionTime: .atDocumentEnd) webView = WKWebView(frame: UIScreen.main.bounds, configuration: config) webView.navigationDelegate = self view.addSubview(webView) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) webView.loadUrl(string: "http://apple.com") } } extension ViewController: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { switch message.name { case getUrlAtDocumentStartScript: print("start: \(message.body)") case getUrlAtDocumentEndScript: print("end: \(message.body)") default: break; } } } extension WKWebView { func loadUrl(string: String) { if let url = URL(string: string) { load(URLRequest(url: url)) } } } extension WKUserScript { class func getUrlScript(scriptName: String) -> String { return "webkit.messageHandlers.\(scriptName).postMessage(document.URL)" } } extension WKWebViewConfiguration { func addScript(script: String, scriptHandlerName:String, scriptMessageHandler: WKScriptMessageHandler, injectionTime:WKUserScriptInjectionTime) { let userScript = WKUserScript(source: script, injectionTime: injectionTime, forMainFrameOnly: false) userContentController.addUserScript(userScript) userContentController.add(scriptMessageHandler, name: scriptHandlerName) } } 

的Info.plist

添加你的Info.plist传输安全设置

 <key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict> 

结果

在这里输入图像说明

资源

文档对象属性和方法

它可能不是一个理想的方法,但根据您的使用情况,您可以重新加载WKWebView感染用户脚本后:

 NSString *scriptSource = @"alert('WKWebView JS Call!')"; WKUserScript *userScript = [[WKUserScript alloc] initWithSource:scriptSource injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES]; [wkWebView.configuration.userContentController addUserScript:userScript]; [wkWebView reload]; 

这里有一些对我有用的东西:

在WKWebView上创build一个定义“runJavaScriptInMainFrame:”方法的扩展。 在扩展方法中,使用NSInvocationOperation来调用未logging的'_runJavaScriptInMainFrame:'方法。

 extension WKWebView { func runJavaScriptInMainFrame(#scriptString: NSString) -> Void { let selector : Selector = "_runJavaScriptInMainFrame:" let invocation = NSInvocationOperation(target: self, selector: selector, object: scriptString) NSOperationQueue.mainQueue().addOperation(invocation) } } 

要使用,请拨打:

 webview.runJavacriptInMainFrame:(scriptString: "some javascript code") 

感谢Larsaronen提供了WKWebView的私有API的链接。

我只是开始自己挖掘WKWebView API,所以这可能不是最好的方法,但我认为你可以用下面的代码来实现:

 NSString *scriptSource = @"console.log('Hi this is in JavaScript');"; WKUserScript *userScript = [[WKUserScript alloc] initWithSource:scriptSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]; [myWKController addUserScript:userScript]; 

(来自WWDC'14讲话)

有传闻说,这是一个错误,因为有一个私人函数类似于(?)在UIWebView中公开可用于从obj-C评估JavaScript。