60

iOS 8 で WKWebView を使用して、ネイティブ側から JavaScript 関数を実行したり、ネイティブ側から JavaScript に通信したりするにはどうすればよいですか? UIWebView のstringByEvaluatingJavaScriptFromString:.

(オブジェクトで を使用- addScriptMessageHandler:name:してconfiguration.userContentControllerJS からネイティブへの通信を許可できますが、反対の方向を探しています。)

4

7 に答える 7

75

(ここで質問した直後にレーダーを提出しました。)

数日前に新しいメソッドが追加されました (指摘してくれた jcesarmobile に感謝します)。

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

このメソッドは、iOS 8 ベータ 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; 

ドキュメントはhttps://developer.apple.com/documentation/webkit/wkwebview/1415017-evaluatejavascriptから入手できます。

于 2014-06-14T23:15:36.650 に答える
26

詳細

  • Xcode 9.1、スウィフト 4
  • Xcode 10.2 (10E125)、スウィフト 5

説明

スクリプトは、WKWebView に表示されるページに挿入されます。このスクリプトはページの URL を返します (ただし、別の JavaScript コードを記述できます)。これは、スクリプト イベントが Web ページで生成されることを意味しますが、関数で処理されます。

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

解決

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)
    }
}

使用法

WKWebView の初期化

 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)
    }
}

Info.plist

Info.plist トランスポート セキュリティ設定を追加します

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

結果

ここに画像の説明を入力

資力

Document オブジェクトのプロパティとメソッド

于 2016-11-21T22:13:33.300 に答える
6

これは理想的な方法ではないかもしれませんが、ユースケースによっては、ユーザー スクリプトを感染させた後に WKWebView をリロードすることができます。

NSString *scriptSource = @"alert('WKWebView JS Call!')";

WKUserScript *userScript = [[WKUserScript alloc] initWithSource:scriptSource
                                                  injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
                                               forMainFrameOnly:YES];

[wkWebView.configuration.userContentController addUserScript:userScript];
[wkWebView reload];
于 2014-06-07T01:58:20.243 に答える
2

これが私のために働いているものです:

「runJavaScriptInMainFrame:」メソッドを定義する WKWebView で拡張機能を作成します。拡張メソッドでは、NSInvocationOperation を使用して、文書化されていない「_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")

WKWebView のプライベート API へのリンクを提供してくれた Larsaronen に感謝します。

于 2014-06-13T14:26:23.173 に答える
0

obj-C から JavaScript を評価するために UIWebView で公開されているものと同様の (?) プライベート関数があるため、これはバグであるという噂があります。

于 2014-06-06T00:41:21.090 に答える
0

私は自分で 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のトークより)

于 2014-06-05T00:04:26.083 に答える