web-dev-qa-db-ja.com

iOS WKWebViewを使用して、JavaScriptコールバックを挿入してonclickイベントを検出する方法

WKWebViewを使用して、3つのボタンを含むHTMLを含むWebサイトを表示しています。特定のボタンがクリックされたときに、ネイティブアプリでSwiftコードを実行したい。

HTMLについて

3つのボタンは次のようになります。

<input type="button" value="Edit Info" class="button" onclick="javascript:GotoURL(1)">
<input type="button" value="Start Over" class="button" onclick="javascript:GotoURL(2)">
<input type="button" value="Submit" class="button" onclick="javascript:GotoURL(3)">

呼び出すGotoURL関数は次のようになります。

function GotoURL(site)
{
    if (site == '1')
        document.myWebForm.action = 'Controller?op=editinfo';
    if (site == '2')
        document.myWebForm.action = 'Controller?op=reset';
    if (site == '3')
        document.myWebForm.action = 'Controller?op=csrupdate';
    document.myWebForm.submit();
}

現在のWKWebView実装

WebViewのボタンのいずれかをクリックすると、WKNavigationDelegateでこの関数が呼び出されます。

func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
    // ...?
}

しかし、もちろん、navigationは不透明であるため、ユーザーが3つのボタンのどれをクリックしたかに関する情報は含まれていません。

このボタンがクリックされたことを検出する最も簡単な方法は何ですか?

ユーザーがSubmitをクリックしたときに応答し、他のボタンの押下を無視したい。

Stack OverflowでWKUserContentControllerを使用する他のアプローチもいくつかありますが、Webサイトで次のような呼び出しを行う必要があります。

window.webkit.messageHandlers.log.postMessage("submit");

私はこのウェブサイトを管理していないので、ソースコードにこの行を追加することはできません。また、WKWebViewを使用して正しい場所に挿入する最良の方法がわかりません。

14
Aaron Brager

ユーザースクリプトは、ドキュメントの読み込みの開始時またはドキュメントの読み込みが完了した後にWebページに挿入するJSです。ユーザースクリプトは、Webページのクライアント側のカスタマイズを許可し、イベントリスナーの挿入を許可し、さらにネイティブアプリにコールバックできるスクリプトの挿入にも使用できるため、非常に強力です。次のコードスニペットは、ドキュメントの読み込みの最後に挿入されるユーザースクリプトを作成します。ユーザースクリプトは、WKWebViewConfigurationオブジェクトのプロパティであるWKUserContentControllerインスタンスに追加されます。

_// Create WKWebViewConfiguration instance
  var webCfg:WKWebViewConfiguration= WKWebViewConfiguration()

  // Setup WKUserContentController instance for injecting user script
  var userController:WKUserContentController= WKUserContentController()

  // Get script that's to be injected into the document
  let js:String= buttonClickEventTriggeredScriptToAddToDocument()

  // Specify when and where and what user script needs to be injected into the web document
  var userScript:WKUserScript =  WKUserScript(source: js, 
                                         injectionTime: WKUserScriptInjectionTime.AtDocumentEnd
                                         forMainFrameOnly: false)

  // Add the user script to the WKUserContentController instance
  userController.addUserScript(userScript)

  // Configure the WKWebViewConfiguration instance with the WKUserContentController
  webCfg.userContentController= userController;
_

Webページは、window.webkit.messageHandlers.<name>.postMessage (<message body>)メソッドを介してネイティブアプリにメッセージを投稿できます。ここで、「名前」は、ポストバックされるメッセージの名前です。 JSは任意のJSオブジェクトをメッセージ本文としてポストバックでき、JSオブジェクトは対応するSwift=ネイティブオブジェクトに自動的にマッピングされます。次のJSコードスニペットは、ボタンクリックイベントが発生したときにメッセージをポストバックしますIDが「ClickMeButton」のボタン。

_varbutton = document.getElementById("clickMeButton");
button.addEventListener("click", function() {
            varmessageToPost = {'ButtonId':'clickMeButton'};
            window.webkit.messageHandlers.buttonClicked.postMessage(messageToPost);
        },false);
_

Webページによって投稿されたメッセージを受信するには、ネイティブアプリがWKScriptMessageHandlerプロトコルを実装する必要があります。プロトコルは、単一の必須メソッドを定義します。コールバックで返されたWKScriptMessageインスタンスは、ポストバックされているメッセージの詳細を照会できます。

_func userContentController(userContentController: WKUserContentController,
                           didReceiveScriptMessage message: WKScriptMessage) {

        if let messageBody:NSDictionary= message.body as? NSDictionary{
            // Do stuff with messageBody
        }

    }
_

最後に、WKScriptMessageHandlerプロトコルを実装するネイティブクラスは、次のように自身をWKWebViewのメッセージハンドラとして登録する必要があります。

_// Add a script message handler for receiving  "buttonClicked" event notifications posted 
// from the JS document
userController.addScriptMessageHandler(self, name: "buttonClicked")
_
6
Kousic

Webビューで evaluateJavaScript(_:) を使用すると、いつでもソースコードを挿入できます。そこから、ボタンのイベントハンドラーを置き換える(メッセージを投稿してから新しいハンドラーで元の関数を呼び出す)か、ボタンまたはクリックイベントをキャプチャしてメッセージを投稿する先祖要素にイベントハンドラーを追加します。 (元のイベントハンドラも実行されます。)

document.getElementById("submit-button").addEventListener("click", function () {
   window.webkit.messageHandlers.log.postMessage("submit");
});

ボタンにIDがない場合、すべての(バブル)クリックイベントをキャプチャし、イベントターゲットに基づいてメッセージを投稿するハンドラーをドキュメントに追加できます(ボタンのテキストまたは場所を決定要因として使用)。

evaluateJavaScript()関数を使用して、WKWebViewにJSコードを挿入できます。次のJSコードを使用して、onclickボタンのクリックのみを検出するすべてのsubmitイベントをキャプチャできます。元のイベントハンドラーもすべて実行されます-心配しないでください!

document.addEventListener("click", function(e)
{
    e = e || window.event;
    //if(e.target.value == 'Submit') //you can identify this button like this, but better like:
    if(e.target.getAttribute('onclick') == 'javascript:GotoURL(3)')
    //if this site has more elements with onclick="javascript:GotoURL(3)"
    //if(e.target.value == 'Submit' && e.target.getAttribute('onclick') == 'javascript:GotoURL(3)')
    {
        console.log(e.target.value);
        //if you need then you can call the following line on this place too:
        //window.webkit.messageHandlers.log.postMessage("submit");
    }
});

//DO NOT add the next line in your code! It is only for my demo - buttons need this in my demo for execution
function GotoURL(site){}//DO NOT add this line! It is only for my demo!
<input type="button" value="Edit Info" class="button" onclick="javascript:GotoURL(1)">
<input type="button" value="Start Over" class="button" onclick="javascript:GotoURL(2)">
<input type="button" value="Submit" class="button" onclick="javascript:GotoURL(3)">
3
Bharata