WKWebView和JS交互

WKWebView和JS的那些事

最近公司需要将 某网站注入JS,从而实现对其中一些视频添加下载按钮的功能。当然我对JS不太懂,JS是由我们Web前端写的,我要实现的就是打开网站的时候注入JS,并根据里面点击按钮的操作作出响应,JS会给我传一些参数。这里记录下我做的事情,给需要的朋友看下。
在这里插入图片描述
之后我们会用 http://www.test.com 代替你要注入的网站网址。之后自己写的代码做个替换就好。

注入JS (OC调用JS)

最重要的第一步,就是将JS注入。

  - (WKWebView *)myWebView {
      if (!_myWebView) {
          WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
          
          theConfiguration.userContentController = [[WKUserContentController alloc] init];
          [theConfiguration.userContentController addScriptMessageHandler:self name:@"downloader"];
          [theConfiguration.userContentController addScriptMessageHandler:self name:@"console"];
          if(IOSVersion >= 10.0){
              theConfiguration.mediaTypesRequiringUserActionForPlayback = WKAudiovisualMediaTypeNone;
          }
          
          theConfiguration.allowsPictureInPictureMediaPlayback = YES;
          theConfiguration.preferences.javaScriptEnabled = YES;

          theConfiguration.allowsInlineMediaPlayback = YES;

          _myWebView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:theConfiguration];
          _myWebView.contentScaleFactor = 3;
          _myWebView.backgroundColor = [UIColor whiteColor];
          _myWebView.opaque = NO;
          if (@available(iOS 11.0, *)) {
             _myWebView.scrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
          }

          _myWebView.scrollView.bounces = YES;
          
          _myWebView.navigationDelegate = self;
          _myWebView.UIDelegate = self;
     }
      return _myWebView;
  }

以上代码稍后讲到,先讲下注入时机,目前我们的是页面加载成功后一次性注入,在WKWebView加载完成后注入。你也可以每次URL变化都注入一次。

  - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    if ([urlstring isEqualToString:@"http://www.test.com"]) {
          [self injectConsoleJS];
      }
  }

  - (void)injectConsoleJS{
      NSString * js = [NSString stringWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"javascript-ios" withExtension:@"js"] encoding:NSUTF8StringEncoding error:nil] ;
      if (js) {
          [self.myWebView evaluateJavaScript:js completionHandler:^(id _Nullable data, NSError * _Nullable error) {
              SLLog(@"");
          }];
      }
  }

至于JS在OC代码中存在的形式,如果比较少的JS,你可以直接把JS直接赋给一个字符串,但是要注意双引号要用\做反义,最好对JS坐下压缩后再这么做,我是直接从包文件中读取。当然你也可以搞个服务器接口下发JS代码,这样就不用每次更改JS都要打包重新提交审核。

JS调用OC

调用方式,你可以拦截URL从而调用你的原生方法,还可以用WKWebView的新特性MessageHandler来调用原生方法。

所谓JS调用OC,就是OC对JS那边的操作作出反馈。比如点击按钮事件,JS会告诉你用户点击了按钮,赶快作出响应吧。当然他会告诉你对应的参数,也可以不传参。

JS中要用的方法:

window.webkit.messageHandlers.<name>.postMessage(<messageBody>)
支持的格式:Allowed types are NSNumber, NSString, NSDate, NSArray, NSDictionary, and NSNull.

首先我们JS代码中要在需要调用OC的位置加上如下代码:(我这里传的是一个字典)

window.webkit.messageHandlers.downloader.postMessage({"link":link, "code":code})

解释一下,上面是和安卓JS不太一样的地方,这个很重要的地方:
上面我们创建WKWebView的时候,有一句代码如下:

[theConfiguration.userContentController addScriptMessageHandler:self name:@"downloader"];

有了这句,我们就实现了对JS消息的监听了。如果JS需要调用原生方法,就会通过以下方法监听到。

(当然我们需要我们controller实现协议)

  #pragma mark WKScriptMessageHandler

  - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
      if ([message.name isEqualToString:@"downloader"]) {
          NSDictionary *dic = message.body;
          if (dic) {
              NSString *videoURL = dic[@"link"];
              NSString *code = dic[@"code"];
              /*
              有了参数,调用你的原生方法。
			 */
          }
      }else {
      }
  }

页面消失之前记得移除这个监听。不然强引用self就会无法释放。内存泄漏的。?

[self.myWebView.configuration.userContentController removeScriptMessageHandlerForName:@"downloader"];

至此,JS调用原生的过程结束。

小问题

最后说一个WKWebView的一个小问题。WKWebView 对于打开target="_blank"的页面 不支持的,怎么办呢。?,当时我被这个问题折腾的够呛,为什么点击html里的一个页面图片没反应呢,原来是WKWebView根本不处理,我们要自己处理,帮助他完成,或者干脆你用原生的打开一个新的页面也行。
首先要实现协议UIDelegate,主要用到这个回调方法。

/*! @abstract Creates a new web view.
 @param webView The web view invoking the delegate method.
 @param configuration The configuration to use when creating the new web
 view. This configuration is a copy of webView.configuration.
 @param navigationAction The navigation action causing the new web view to
 be created.
 @param windowFeatures Window features requested by the webpage.
 @result A new web view or nil.
 @discussion The web view returned must be created with the specified configuration. WebKit will load the request in the returned web view.

 If you do not implement this method, the web view will cancel the navigation.
 */
- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
  if (!navigationAction.targetFrame.isMainFrame) {
      NSURL * url = navigationAction.request.URL;
	  [webView loadRequest:navigationAction.request];
	  /*
	  或者原生的打开新的Controller界面
	  */
  }
  return nil;
 }

在这里插入图片描述


版权声明:本文为shanglanxin原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。