在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。