iOS 開発の話 WKWebView は、H5 Web 画像にクリック イベントを追加してネイティブ画像プレビューを実現します

     これはコミック WKWebView の続きです。この記事では、主に、クリックして画像へのプレビューを実現するために WKWebView に Js を挿入する必要性を実装します。早速、完成品を見てみましょう。

この要件を実現するには、間違いなく、Js と WKWebView 間の値の転送や相互呼び出しなどの対話的な操作を実現する必要があります。このトピックに関する記事はインターネット上に大量にありますが、この記事ではそのうちのいくつかのみを使用します。Js を注入してクリック イベントを動的に img に追加し、値を WKWebView に渡し、イベントにネイティブに応答してプレビューを実装するためのパラメーターを取得します。 . 最後のポイントは、値を渡すことです。アイデアを整理するには、アプリで Scheme、リクエスト インターセプト、JavaScriptCore、および WKScriptMessageHandler を使用できます。

       この 2 つの文に関する多くの記事を次に示します。インターネット上の多くの記事は、元のコピーをテストしていません。例: DocumentView.webview.mainframe.javaScriptContext は UIWEBVIEW で使用できますが、WKWebview では直接折りたたまれます。これは JavaScriptCore を使用する上で必要な部分ですが、この穴のせいで多くの時間が無駄になりました。したがって、ここでのオプションはリクエスト インターセプトと WKScriptMessageHandler の 2 つだけです。この記事では主に WKScriptMessageHandler を使用します。主な参照コードは以下のとおりです。

1. WKWebViewを定義し、WKNavigationDelegateプロトコルを指定します

private lazy var wkWebView:WKWebView = {[unowned self] in
        let _wk = WKWebView.init(frame: .zero)
        _wk.navigationDelegate = self
       
        return _wk
   }()

2. WKNavigationDelegate プロトコルを実装し、Js イベントを注入します (注: jQuery ライブラリを注入するときは、jQuery 公式 Web サイトからファイルをダウンロードしてプロジェクトに追加する必要があります。そうしないと注入できません)。


//MARK: - WKNavigationDelegate
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.15) {
            self.pourIntoJsFor(WKWebView: webView)
        }
    }
    
/// 注入Js
private func pourIntoJsFor(WKWebView webView:WKWebView){
        //注入jQuery库(由于很多H5使用的VUE或者Ajax等技术异步加载渲染,因此此处需要借助jQuery库给一步渲染的img动态绑定事件,如果是静态H5或者H5中已有该文件则无需注入jQuery库)
        let jQueryPath = Bundle.main.path(forResource: "jQuery-3.5.1.mini", ofType: "js")
        if let jQueryDat:Data = try? Data.init(contentsOf: URL.init(fileURLWithPath: jQueryPath!)) {
            webView.evaluateJavaScript(String.init(data: jQueryDat, encoding: .utf8)!) { [self] (_:Any?, error:Error?) in
                print("jQuery注入\(error == nil ? "成功":"失败")")
                
                if error == nil {
                    //注入图片点击的脚本事件
                    let strJs = String.init(format:"var imgUrl='';var objs=document.getElementsByTagName('img');for(var i=0;i<objs.length;i++){imgUrl+=objs[i].src+',';$(objs[i]).on('click',function(){window.webkit.messageHandlers.%@.postMessage({curl:this.src,imgUrl});});}",js_message_handler_name)

                    print("strJs:\(strJs)")
                    webView.evaluateJavaScript(strJs) { (_:Any?, error:Error?) in
                        print("Js注入\(error == nil ? "成功":"失败")")
                    }
                }
            }
        }
    }

3. Jsインジェクションオブジェクトの初期化と追加

    private let js_message_handler_name = "CommunityJSObj"

    override init(frame: CGRect) {
        super.init(frame: frame)
        self.addSubview(self.wkWebView)
        
        //注意在这里注入JS对象名称
        self.wkWebview.configuration.userContentController.add(self, name: js_message_handler_name)
    }

注:window.webkit.messageHandlers.xxx.postMessage は、大文字と小文字が一貫した特定の書き込みメソッドです。中央の xxx はカスタム インジェクション オブジェクト名です。この記事では、 js_message_handler_name 定数オブジェクト

パラメータがない場合は次のように記述できます:

window.webkit.messageHandlers.xxx.postMessage();

パラメータは次のように記述できます:

window.webkit.messageHandlers.xxx.postMessage({body:パラメータ値 1});

または、次のように書きます。

window.webkit.messageHandlers.xxx.postMessage({パラメータ名1:パラメータ値1});

複数のパラメータは次のように記述できます:

window.webkit.messageHandlers.xxx.postMessage({パラメータ名1:パラメータ値1,パラメータ値2,...});

4. H5 に挿入された img click イベントに応答する WKScriptMessageHandler プロトコルを実装します。

//MARK: - WKScriptMessageHandler
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            
        /**
         message:WKScriptMessage对象
         message.name:注入JS对象名称
         message.body:js回传参数
         */
        print(message)
        print(message.name)
        print(message.body)
        
        //图片预览
        if message.name == js_message_handler_name {
            guard let _dicTemp = message.body as? [String:String] else {
                return
            }
            
            guard let curl = _dicTemp["curl"],let imgUrls:[String] = _dicTemp["imgUrl"]?.split(separator: ",").compactMap({ "\($0)" }) else {
                return
            }
            
            print("curl:\(curl),imgUrls:\(imgUrls)")         
        }
  }

画像をクリックしたときの応答情報は次のように出力されます。

5. オブジェクトを破壊する

deinit {
        self.wkWebview.configuration.userContentController.removeScriptMessageHandler(forName:js_message_handler_name)
        
        //清除wkWebView缓存
        let dt = WKWebsiteDataStore.allWebsiteDataTypes()
        let date = Date.init(timeIntervalSince1970: .zero)
        WKWebsiteDataStore.default().removeData(ofTypes: dt, modifiedSince: date) {
            print("wkWebView 缓存清除Ok")
        }
    }

なお、リクエストインターセプトについては後ほどブログで書きますが、この機能もこの方法で実現できます。

この記事はこれで終わりです!以上です。ご不明な点がございましたら、メッセージをお願いいたします。

 

おすすめ

転載: blog.csdn.net/yimiyuangguang/article/details/114642196