Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/458.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
从WKWebView中的本机代码调用JavaScript函数_Javascript_Ios_Webkit_Wkwebview - Fatal编程技术网

从WKWebView中的本机代码调用JavaScript函数

从WKWebView中的本机代码调用JavaScript函数,javascript,ios,webkit,wkwebview,Javascript,Ios,Webkit,Wkwebview,使用iOS 8中的WKWebView,如何从本机端运行JavaScript函数,或者如何从本机端与JavaScript通信?似乎没有类似于UIWebView的stringByEvaluatingJavaScriptFromString:的方法 (我可以在配置.userContentController对象上使用-addScriptMessageHandler:name:来允许从JS到本机的通信,但我正在寻找相反的方向。)我自己刚开始研究WKWebView API,所以这可能不是最好的方法,但我认

使用iOS 8中的WKWebView,如何从本机端运行JavaScript函数,或者如何从本机端与JavaScript通信?似乎没有类似于UIWebView的
stringByEvaluatingJavaScriptFromString:
的方法


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

我自己刚开始研究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篇演讲)

有传言说这是一个bug,因为UIWebView中有一个类似于(?)的私有函数,用于评估obj-C中的javascript。

这可能不是一个理想的方法,但根据您的用例,您可以在感染用户脚本后重新加载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上创建一个扩展,该扩展定义一个“RunJavaScriptInInframe:”方法。在扩展方法中,使用NSInvocationOperation调用未记录的“\u runjavascriptinInframe:”方法

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的链接。

(我在这里提问后不久就提交了一个雷达。)

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

添加
-[WKWebView evaluateJavaScript:completionHandler:][/code>

该方法在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; 
文档可在此处获得:。

详细信息
  • 代码9.1,Swift 4
  • 代码10.2(10E125),Swift 5
描述 脚本将插入到将在WKWebView中显示的页面中。此脚本将返回页面URL(但您可以编写另一个JavaScript代码)。这意味着脚本事件在网页上生成,但将在我们的函数中处理:

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {...}
解决方案 用法 Init WKWebView

捕捉事件

完整代码示例 Info.plist 添加您的Info.plist传输安全设置

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

资源

一次失败的尝试:我尝试了
webView.loadRequest(NSURLRequest(URL:NSURL(string:“javascript:alert(1)”)))
但似乎什么也没有发生。我为此提交了一个雷达——标记为重复:将其作为功能请求归档在此处,似乎他们也修复了它。抱歉–我不想在加载时运行脚本,但后来为了回应本土的一些行动,这实际上是行不通的;addUserScript只在页面加载之前起作用。是的,这有一个私有API:但是为什么他们要将其私有化。。似乎是有意为之。这会仅为此重新加载整个页面吗?这不起作用,因为您正在处理WKWebView配置的副本(属性具有copy标志)。您唯一可以设置配置的时间是在WKWebView init。在应用商店审批流程中,如果使用未记录/私有API,这不会被拒绝吗?@Larsaronen,是的,我认为您是正确的:此代码很可能会导致应用被拒绝。我想在WKWebView中测试一些JavaScript,所以将其用作临时修复。应该提到,这不应该用于生产应用程序。谢谢你的提醒!为什么不完成此线程并选择合适的答案,让读者知道有答案:-)我想在我的webview url中添加用户名和密码,以便用户自动登录。我的应用程序你可以建议我如何使用你的代码吗?你可能还需要在你的WKWebView中启用Javascript(默认情况下现在似乎已被禁用)
let preferences=WKPreferences()preferences.javaScriptEnabled=true let configuration=WKWebViewConfiguration()configuration.preferences=preferences webView=WKWebView(frame:.zero,configuration:configuration)
仅供参考
addScript
不再是了吗
WKWebViewConfiguration
上的方法。我想在我的webview url中添加用户名和密码,以便用户自动登录。我的应用能否请你建议我如何使用你的代码。我不知道你的授权机制,但通常人们使用Alamofire库或UrlSession。另外,您是否尝试加载webView.loadUrl(字符串:“您的带有登录名和密码的URL”),而不是使用config.add,我使用了webView.configuration.userContentController.add(self,name:“handlernamehere”)另外,请确保启用javascript:webView.configuration.preferences.javaScriptEnabled=true
 let config = WKWebViewConfiguration()
 config.add(script: .getUrlAtDocumentStartScript, scriptMessageHandler: self)
 config.add(script: .getUrlAtDocumentEndScript, scriptMessageHandler: self)

 webView = WKWebView(frame:  UIScreen.main.bounds, configuration: config)
 webView.navigationDelegate = self
 view.addSubview(webView)
extension ViewController: WKScriptMessageHandler {
    func userContentController (_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if  let script = WKUserScript.Defined(rawValue: message.name),
            let url = message.webView?.url {
                switch script {
                    case .getUrlAtDocumentStartScript: print("start: \(url)")
                    case .getUrlAtDocumentEndScript: print("end: \(url)")
                }
        }
    }
}
import UIKit
import WebKit

class ViewController: UIViewController, WKNavigationDelegate {

    private var webView = WKWebView()

    override func viewDidLoad() {
        super.viewDidLoad()

        let config = WKWebViewConfiguration()
        config.add(script: .getUrlAtDocumentStartScript, scriptMessageHandler: self)
        config.add(script: .getUrlAtDocumentEndScript, scriptMessageHandler: self)

        webView = WKWebView(frame:  UIScreen.main.bounds, configuration: config)
        webView.navigationDelegate = self
        view.addSubview(webView)
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        webView.load(urlString: "http://apple.com")
    }
}

extension ViewController: WKScriptMessageHandler {
    func userContentController (_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if  let script = WKUserScript.Defined(rawValue: message.name),
            let url = message.webView?.url {
                switch script {
                    case .getUrlAtDocumentStartScript: print("start: \(url)")
                    case .getUrlAtDocumentEndScript: print("end: \(url)")
                }
        }
    }
}

extension WKWebView {
    func load(urlString: String) {
        if let url = URL(string: urlString) {
            load(URLRequest(url: url))
        }
    }
}

extension WKUserScript {
    enum Defined: String {
        case getUrlAtDocumentStartScript = "GetUrlAtDocumentStart"
        case getUrlAtDocumentEndScript = "GetUrlAtDocumentEnd"

        var name: String { return rawValue }

        private var injectionTime: WKUserScriptInjectionTime {
            switch self {
                case .getUrlAtDocumentStartScript: return .atDocumentStart
                case .getUrlAtDocumentEndScript: return .atDocumentEnd
            }
        }

        private var forMainFrameOnly: Bool {
            switch self {
                case .getUrlAtDocumentStartScript: return false
                case .getUrlAtDocumentEndScript: return false
            }
        }

        private var source: String {
            switch self {
            case .getUrlAtDocumentEndScript, .getUrlAtDocumentStartScript:
                return "webkit.messageHandlers.\(name).postMessage(document.URL)"
            }
        }

        fileprivate func create() -> WKUserScript {
            return WKUserScript(source: source,
                                injectionTime: injectionTime,
                                forMainFrameOnly: forMainFrameOnly)
        }
    }
}

extension WKWebViewConfiguration {
    func add(script: WKUserScript.Defined, scriptMessageHandler: WKScriptMessageHandler) {
        userContentController.addUserScript(script.create())
        userContentController.add(scriptMessageHandler, name: script.name)
    }
}
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>