iOS WebView与Native交互

我们在项目中不可避免的要使用到WebView,一般的用法就是WebView直接加载URL,做一些基本展示操作。但是对于一些特定的需求或逻辑,我们可能就需要WebView传递一些数据到Native,由Native来对数据做处理,比如有跨域限制或拦截WebView请求的需求时候。

备注:这里的Native是相对于JavaScript来说的,是指OC代码。

1. UIWebView

我们这里要做到双向交互,要求UIWebView加载的Html可调用Naitve函数,Native可调用Html函数。我在这里使用的是Apple官方的JavaScriptCore库。

1.1 Native调用JavaScript

在Native中我们调用JS的函数,函数名为showAlert

OC代码:

- (void)webViewDidFinishLoad:(UIWebView *)webView{
  NSString *result = [webView stringByEvaluatingJavaScriptFromString:@"showAlert('webViewDidFinishLoad')"];
  NSLog(@"html返回给native的返回值是:%@",result);
}

对应的JS代码:

<script>
        function showAlert (str) {
            alert(str);
            return 'html的返回值';
        }
    </script>

1.2 JavaScript调用Native

OC代码:

#import <CoreLocation/CoreLocation.h>
//这里最主要是创建一个JSContext
JSContext *context = [w valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//通过这个context可以将OC中函数添加到JavaScript的运行环境中
context[@"add"] = ^(NSInteger a, NSInteger b){
        return a+b;
    };
//这里就是向JavaScript添加了一个add函数,将JavaScript传入的两个参数和返回至调用的JavaScript中。

对应的JS代码:

<button onclick="alert(add(2,3))">这个是注入module</button>

当然也可以将JSBridage抽象出来,统一在WebView中进行初始化。这样可以在JS中通过一个对象统一掉用Native的函数,可以避免JavaScript函数与Native的OC函数名冲突.
在OC中这样写:

#import <Foundation/Foundation.h>
#import "JSBridgeModule.h"
#import "StringUtil.h"
#import <JavaScriptCore/JavaScriptCore.h>

@implementation JSBridgeModule

- (NSString *)getTime:(NSString *)str{
  NSLog(@"getTime native log:%@",str);
  return [StringUtil getCurrentTime];
}

- (void)initJSBridge:(JSContext *)jsContext{
  [jsContext setObject:self forKeyedSubscript:@"bridge"];//这里将整个JSBridgeModule赋给了JavaScript运行环境,在JavaScript便可以使用bridge.函数名来调用Native函数
  jsContext.exceptionHandler = ^(JSContext* context, JSValue* exceptionValue) {
    context.exception = exceptionValue;
    NSLog(@"js页面异常了:%@", exceptionValue);
  };
}

- (void)callJSFunction:(JSContext *)jsContext{
  JSValue *result = [jsContext evaluateScript:@"showAlert('这是native传递个html的数据')"];
  NSLog(@"html返回给native的返回值是:%@",result);
}

@end

对应的JS使用方法

<button onclick="alert(bridge.getTime('这个是注入module'))">这个是注入module</button>

2. WKWebView

在iOS 8.0中更新了WKWebView来替代UIWebView,WKWebView的性能和效率比UIWebView要强。

这里有个插曲,,由于我们项目的需要,我们要对WebView加载的Html进行网络请求的拦截,只允许Html中的请求加载按照我们规范的地址。我对UIWebView和WKWebView分别作了研究,注册WebView的请求delegate是行不通的,我在UIWebView中重写URlSession来拦截所有的网络请求,这样可以达到拦截UIWebView请求目的,但是这个方法在WKWebView中行不通,因为WKWebView和当前项目是不同同一个进程中运行的,所以无法拦截WKWebView中的网络请求,所以这条路行不通。我会新写一篇文章详细的讲解这个问题。

下面言归正传,WKWebView中JS与Native的交互

2.1 Native调用JavaScript

这里很简单,和UIWebView差不多

OC代码:

//这是是调用JS的showAlert函数,并传入参数hello
[self.webView evaluateJavaScript:@"showAlert('hello')" completionHandler:nil];

JS代码:

<script>
        function showAlert (str) {
            alert(str);
            return 'html的返回值';
        }
    </script>

2.2 JavaScript调用Native

OC代码:

向WebView中注入可供JS调用对象

WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc]init];
  WKUserContentController *controller = [[WKUserContentController alloc]init];
  [controller addScriptMessageHandler:self name:@"bridge"];
  //这里的bridge对象即为JS调用Native的桥接
  config.userContentController = controller;

注册WebView delegate

//JavaScript与Native的交互
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
  NSDictionary *body = message.body;
  NSLog(@"userContentController message.body:%@",body);
  //这里来判断JS传递过来的数据是否是需要的
  if ([message.name isEqualToString:@"xxx"]) {
    NSString *method = [body valueForKey:@"function"];
    if ([method isEqualToString:@"nativeSetParam"]) {
      NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];
      [userDefault setValue:[body valueForKey:@"value"] forKey:[body valueForKey:@"key"]];
      [userDefault synchronize];
    }
  }
}

对应JS使用代码:

<button onclick="postMessage()">JS调用Native</button>
//必须通过 xxx.postMessage才能将JS的数据发送到Native中,Native在回调中获取JS传递过来的数据
<script>
    var bridge = window.webkit.messageHandlers.bridge;
        function postMessage(){
            alert('要发送消息了');
            var message = {
                'method' : 'hello',
                'param1' : 'param',
            };
            bridge.postMessage(message);
        }
   </script>
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值