UIWebViewとWKWebViewとJSの通信

10101 ワード

概要
UIWebViewは、アプリケーションにWebコンテンツを埋め込み、対話することができますが、重くてメモリの漏洩があります.WKWebViewは、UImitのUIWebViewとAppKitのWebViewの代わりに、統合されたデュアルプラットフォームAPIを提供します.60 fpsまでのスクロールリフレッシュ率と内蔵ジェスチャーで、パフォーマンス、安定性、機能が大幅に向上し、Safariと同じJavaScriptエンジンをサポートしています.
1. UIWebView
1.1ロード方法
//  UIWebView
UIWebView  *webView = [[UIWebView alloc] initWithFrame:self.view.bounds];
webView.delegate = self;
[self.view addSubview:webView];
//  URL
NSURL *url = [[NSURL alloc] initWithString:@"http://www.biadu.com"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[webView loadRequest:request];

ロード方法は3種類ございます
- (void)loadRequest:(NSURLRequest *)request; //    URL
- (void)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;//  HTML  
- (void)loadData:(NSData *)data MIMEType:(NSString *)MIMEType textEncodingName:(NSString *)textEncodingName baseURL:(NSURL *)baseURL;//               NSData

1.2代理コールバック
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;//         ,    YES,        
- (void)webViewDidStartLoad:(UIWebView *)webView;//      
- (void)webViewDidFinishLoad:(UIWebView *)webView;//    
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error;//    

1.3 NativeとJSの相互呼び出し
1.3.1 NativeがJSを呼び出す方法
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
//      JSFunctionName JS      paramete   
[self. webView stringByEvaluatingJavaScriptFromString:@"JSFunctionName('paramete')"]

1.3.2 JS呼び出しNativeでのメソッドJS Native , UI #importはクラスにJSPportプロトコルを追加することができ、JSPportプロトコルクラスを追加すれば、JSを通じてそのメソッド変数などを呼び出すことができます.
//      js  oc  
#define JSExportAs(PropertyName, Selector) \
   @optional Selector __JS_EXPORT_AS__##PropertyName:(id)argument; @required Selector
#endif

たとえば、jsでdoFooを呼び出すと、このクラスの-(void)doFoo:(id)fooメソッドが呼び出されます.
@textblock
    @protocol MyClassJavaScriptMethods 
    JSExportAs(doFoo,
    - (NSString *)doFoo:(NSString *)foo
    );
    @end
@/textblock
//OC          #import 
@property(nonatomic,strong)JSContext *jsContext;

//  JSContext
-(void)configJsContext{
    if (_jsContext)  return;
    _jsContext  = [[self contentWebview] valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    JSExportObject *jsExportObject = [[JSExportObject alloc] init];
    jsExportObject.delegate = self;
    //   JSExport        JSContext  native  doFoo           -(NSString *)doFoo:(id)foo         doFoo   NSString   
    _jsContext[@"native"] = jsExportObject;
}

// OC      native   ,   js   native    
   var s= {
        methodId: i,
        params: p
    };
  native. doFoo(JSON.stringify(s))

2. WKWebView
2.1ロード方法
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
YFMWKWebView *wkWebView = [[YFMWKWebView alloc] initWithFrame:self.view.bounds configuration:config];
wkWebView.navigationDelegate = self;
wkWebView.UIDelegate = self;
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://www.biadu.com"]];
[wkWebView loadRequest:mutableRequest];

ロード方法は4種類あり、基本的にUIWebViewと同じローカルファイルのロードが1つ増えただけです
//iOS 8
- (nullable WKNavigation *)loadRequest:(NSURLRequest *)request;
- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;
//iOS 9
- (nullable WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL API_AVAILABLE(macosx(10.11), ios(9.0));
- (nullable WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL API_AVAILABLE(macosx(10.11), ios(9.0));

2.2エージェントコールバック3 WKNavigationDelegate(メインウィンドウのWebページのロードプロセスと新しいページのロードを行うかどうかを追跡するエージェントメソッドを提供)メインウィンドウのWebページのロードプロセスを追跡
//          
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
//           
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
//           
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
//          
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;

新しいページをページにロードするかどうか
//               
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation{
}
//       ,      
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    decisionHandler(WKNavigationResponsePolicyAllow);
}
//        ,      
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
    decisionHandler(WKNavigationActionPolicyAllow);
}

WKUIdelegate(オリジナルコントロールでWebページを表示する方法でコールバック)は、主にWKWebViewがWebインタフェースを処理する3つのヒントボックス(警告ボックス、確認ボックス、入力ボックス)に使用されます.
//   
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler;
//   
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
//   
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler;

WKScriptMessageHandler(Webページからメッセージを受信するコールバックメソッドを提供)OCでJSの呼び出し情報を受信
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
 NSLog(@"JS     %@   ,     %@",message.name, message.body);
}

2.3 NativeとJSの相互呼び出し
2.3.1 NativeがJSを呼び出す方法
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
//      JSFunctionName JS      paramete   
[wkWebView evaluateJavaScript:@"JSFunctionName(‘paramete’)" completionHandler:^(id item, NSError * error) {
 
}];

2.3.2 JS呼び出しNativeでのメソッドWKWebView JS Native JS呼び出しNativeを行うにはWKWebViewの構成方法が必要です.Nativeでは構成情報を追加し、最後に構成されたconfigをリスト2.1に追加する必要があります.
//    
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
//        
WKUserContentController *controller = wkWebView.configuration.userContentController;
[controller addScriptMessageHandler:self name:@"callMe"];
Native Web JS WKUSerScriptにカスタムJSコードを追加しconfigurationにコミットする.userContentControlで
// [configuration.userContentController addUserScript:self.script];     2.1 
- (WKUserScript *)script {
    if (!_script) {
        NSString *jsString = @"function JSFunctionName(a) {\
           var i = {\
           params: a\
          };\
        return window.webkit.messageHandlers.callMe.postMessage(i);\
        }";
        //   JS      WKUserScript  
        WKUserScript *script = [[WKUserScript alloc] initWithSource:jsString
                                                      injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
                                                   forMainFrameOnly:YES];
        _script = script;
    }
    return _script;
}

JSに以下のメソッドを追加して呼び出す
//         ScriptMessageHandler    name,        
window.webkit.messageHandlers..postMessage()

3.問題解決
一.WKWebViewを使用するときにdeallocでブレークポイントを打つと、解放されていないことに気づいた場合、ループリファレンスの問題(addScriptMessageHandlerメソッドで発生)が発生した可能性があります.WKScriptMessageHandlerをカスタマイズするには、weakメソッドを使用してプロキシメソッドを実装します.ページを離れるときにremoveScriptMessageHandlerForNameというメソッドを追加して削除します
二.WKWebView JSがOCを呼び出して戻り値を取得する問題-(void)userContentController:(WKUserContentController*)userContentController didReceiveScriptMessage:(WKScriptMessage*)messageこのメソッドは戻り値がないので戻り値コールバックは実現できません.方法1:OC呼び出しJSメソッドロジックを借りてJS中のCallbackメソッドを再呼び出してコールバック 2.3を実現するか、または使用する必要がある
方法2:runJavaScriptTextInputPanelWithPromptコールバックを利用して実現できる.まず以下のコードを注入し,主にすべての呼び出しOCメソッドがpromptによって実現されることを実装する.
// [configuration.userContentController addUserScript:self.script];     2.1 
- (WKUserScript *)script {
    if (!_script) {
        NSString *jsString = @"function JSFunctionName(a) {\
           var i = {\
           params: a\
          };\
          return prompt('Native_'+i);\
        }";
        //   JS      WKUserScript  
        WKUserScript *script = [[WKUserScript alloc] initWithSource:jsString
                                                      injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
                                                   forMainFrameOnly:YES];
        _script = script;
    }
    return _script;
}

次にOCでPromptをキャプチャして処理してデータを返す
-(void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler {
    //       
    if (prompt && [prompt hasPrefix:@"Native_"]) {
        completionHandler(        );
    }else{
    //       promot
    }
}```