iOS之H5与原生交互

少年易学老难成,一寸光阴不可轻。

1. 利用UIWebView交互

  iOS7之前通过UIWebView相关代理方法进行通信。原理:通过协议拦截实现h5对原生的调用,通过直接调用js来实现原生对h5的调用。

1.1)原生调用h5

  通过stringByEvaluatingJavaScriptFromString方法可以直接调用一段js代码,并返回字符串类型的返回值。

- (void)openCamera {
    [_webView stringByEvaluatingJavaScriptFromString:@"phoneUrl('http://image1')"];
}

1.2)h5调用原生

  在UIWebView的浏览器的JavaScript中,没有相关的接口可以调用Objective-C的相关方法,一般采用JavaScript在浏览器环境中发出URL请求, Objective-C 截获请求以获取相关请求的思路,在Objective-C中在实现UIWebViewDelegate 时截获请求:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSString *url = request.URL.absoluteString;
    if ([url hasPrefix:@"opencame"]) {
        [self openCamera];
        NSLog(@"打开相机");
        return NO;
    }
    
    NSLog(@"url is %@",request.URL.absoluteString);
    return YES;
}

1.3)原生和h5协调

  h5调用原生:需要约定协议拦截信息的规则;

  原生调用h5:约定接口的规则,接口名和参数;

1.4)h5页面代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>1.利用UIWebView进行交互(系统API)</title>

    <style>
        p{
            background: aqua;
            text-align: center;
            line-height: 50px;
            height: 50px;
        }
    </style>

    <script type="text/javascript">
        function openCamera() {
            window.location.href = "opencame://data=1"
        }
        function phoneUrl(url) {
            alert(url)
        }
    </script>

</head>
<body>

<p onclick="openCamera()">模拟调用原生相机</p>

</body>
</html>

2. 利用JavaScriptCore.framework原生框架

  在iOS7 引入了JavaScriptCore原生框架,使得JavaScriptObjective-C可以互相操作,但这个库不支持iOS6及以前的版本,也不支持iOS8发布的WKWebView,下面会讲到。

2.1)JavaScriptCore简介

  JavaScriptCore 是 JS 引擎,通常会被叫做虚拟机,专门设计来解释和执行 JS 代码。我们首先了解下JavaScriptCore框架里创建的常见接口和协议:

JSVirtualMachine:为 JS 的运行提供了底层资源,虚拟机是线程安全; 
JSContext:给JavaScript提供运行的上下文环境,通过evaluateScript:方法就可以执行一JS代码,这里可以用来管理对象,添加方法等;
JSValue:JavaScript和Objective-C数据和方法交互的桥梁,封装了JS与ObjC中的对应的类型,以及调用JS的API等;
JSManagedValue:可以处理内存管理中的一些特殊情形,它能帮助引用技术和垃圾回收这两种内存管理机制之间进行正确的转换
JSExport:这是一个协议,如果采用协议的方法交互,自己定义的协议必须遵守此协议;实现 JSExport 协议可以开放 OC 类和它们的实例方法,类方法,以及属性给 JS 调用

2.2)JSVirtualMachine

  一个 JSVirtualMachine 的实例就是一个完整独立的 JS 的执行环境,为 JS 的执行提供底层资源。这个类主要用来做两件事情:

  • 实现并发的 JavaScript 执行;

  • JavaScript 和 Objective-C 桥接对象的内存管理;

  相关的接口如下:

NS_CLASS_AVAILABLE(10_9, 7_0)
@interface JSVirtualMachine : NSObject

- (instancetype)init;

// 进行内存管理
- (void)addManagedReference:(id)object withOwner:(id)owner;
- (void)removeManagedReference:(id)object withOwner:(id)owner;

@end

  每一个 JSVirtualMachine 可以包含多个 JS 上下文(JSContext 对象)。同一个虚拟机下不同的上下文之间可以相互传值(JSValue对象)。

  然而,每个虚拟机都是完整且独立的,有其独立的堆空间和垃圾回收器(Garbage Collector ),GC 无法处理别的虚拟机堆中的对象,因此你不能把一个虚拟机中创建的值传给另一个虚拟机。

2.3)JSContext

  一个 JSContext 表示了一次 JS 的执行环境。我们可以通过创建一个 JSContext 去调用 JS 脚本,访问一些 JS 定义的值和函数,同时也提供了让 JS 访问 Native 对象方法的接口。

  一个 JSContext 对象对应了一个全局对象。例如 Web 浏览器中的 JSContext ,其全局对象就是 Window 对象。在其他环境中,全局对象也承担了类似的角色,用来区分不同的 JavaScript Context 的作用域。

  相关接口如下:

JS_EXPORT API_AVAILABLE(macos(10.9), ios(7.0))
@interface JSContext : NSObject

// 初始化,可以指定一个虚拟机,如果没有指定底层默认创建一个
- (instancetype)init;
- (instancetype)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine;

// 执行 JS 脚本,返回值是 JS 中最后生成的一个值,sourceURL 认作其源码 URL,用作标记
- (JSValue *)evaluateScript:(NSString *)script;
- (JSValue *)evaluateScript:(NSString *)script
              withSourceURL:(NSURL *)sourceURL API_AVAILABLE(macos(10.10), ios(8.0));

// 获取当前执行的 JavaScript 代码的 context
+ (JSContext *)currentContext;

// 获取当前执行的 JavaScript function
+ (JSValue *)currentCallee API_AVAILABLE(macos(10.10), ios(8.0));

// 获取当前执行的 JavaScript 代码的 this
+ (JSValue *)currentThis;

// 获取当前 context 回调函数的参数
+ (NSArray *)currentArguments;

// 获取当前 context 的全局对象
@property (readonly, strong) JSValue *globalObject;

// 用于 JavaScript 执行异常
@property (strong) JSValue *exception;
@property (copy) void(^exceptionHandler)(JSContext *context, JSValue *exception);

// 获取当前虚拟机
@property (readonly, strong) JSVirtualMachine *virtualMachine;

// 标记当前 context 
@property (copy) NSString *name API_AVAILABLE(macos(10.10), ios(8.0));

@end

2.3.1)OC调用JS

  Objective-C可以使用JSContext的evalueScript()方法直接调用JavaScript代码:

  直接调用JS代码(本地上下文环境):

// 001 - 直接调用h5代码
- (void)evaluateScript {
    // 一个JSContext对象,就类似于JS中的window,只需要创建一次即可。
    _jsContext1 = [[JSContext alloc] init];
    //  _jsContext1可以直接执行JS代码。
    [_jsContext1 evaluateScript:@"var num = 10"];
    [_jsContext1 evaluateScript:@"var squareFunc = function(value) { return value * value }"];
    // 计算正方形的面积
    JSValue *square = [_jsContext1 evaluateScript:@"squareFunc(num)"];

    // 也可以通过下标的方式获取到方法
    JSValue *squareFunc = _jsContext1[@"squareFunc"];
    JSValue *value = [squareFunc callWithArguments:@[@"20"]];
    NSLog(@"%@", square.toNumber); // 100
    NSLog(@"%@", value.toNumber); // 400
}

  直接调用JS代码(获取HTML上下文环境):

// 002 - 获取网页JSContext 调用js代码
- (void)OCEvaluateScript{
    // 获取网页的JSContext对象
    JSContext *jsContext = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];

    [jsContext evaluateScript:@"changeBackgroundColor()"];
}

2.3.2)JS调用OC

  各种数据类型可以转换,Objective-C的Block也可以传入JSContext中当做JavaScript的方法使用。在JavaScript中可以调用。

// 003 - JS->OC(注入Block到JSContext中当做JavaScript的方法使用,在JavaScript中可以调用)- (void)addFunctionToJS {    //本地上下文环境    //JSContext *context = [[JSContext alloc] init];        //获取HTML上下文环境    JSContext *context = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];    context[@"log"] = ^() {        NSLog(@"+++++++Begin Log+++++++");        NSArray *args = [JSContext currentArguments];        for (JSValue *jsValue in args) {            NSLog(@"参数 = %@", jsValue);        }        JSValue *this = [JSContext currentThis];        NSLog(@"this: %@",this);        NSLog(@"-------End Log-------");    };    // 执行JS代码 - 无意义,模拟效果    [context evaluateScript:@"log('Message Time', { name:'world', length:5 });"];}//+++++++Begin Log+++++++//参数 = Message Time//参数 = [object Object]//this: [object Window]//-------End Log-------

  通过Block成功的在JavaScript调用方法回到了Objective-C,而且依然遵循JavaScript方法的各种特点,比如方法参数不固定。也因为这样,JSContext提供了类方法来获取参数列表(+ (JSContext *) currentArguments;)和当前调用该方法的对象(+ (JSValue *)currentThis)。对于"this"
,输出的内容是Window或者GlobalObject,这也是JSContext对象方法- (JSValue *)globalObject;
所返回的内容。因为我们知道在JavaScript里,所有全局变量和方法其实都是一个全局变量的属性,在浏览器中是window,在JavaScriptCore是什么就不得而知了。

2.4)JSValue

  JSValue 实例是一个指向 JS 值的引用指针。我们可以使用 JSValue 类,在 OC 和 JS 的基础数据类型之间相互转换。你也可以使用这个类去创建包装了自定义类的 Native 对象的 JS 对象,或者是那些由 Native 方法或者 Block 实现的 JS 函数。

  在 JSCore 中,JSValue 自动做了 OC 和 JS 的类型转换:

iOS之H5与原生交互

  相关接口如下:

NS_CLASS_AVAILABLE(10_9, 7_0)@interface JSValue : NSObject@property (readonly, strong) JSContext *context;+ (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context;+ (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context;+ (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context;+ (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context;+ (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context;+ (JSValue *)valueWithNewObjectInContext:(JSContext *)context;+ (JSValue *)valueWithNewArrayInContext:(JSContext *)context;+ (JSValue *)valueWithNewRegularExpressionFromPattern:(NSString *)pattern flags:(NSString *)flags inContext:(JSContext *)context;+ (JSValue *)valueWithNewErrorFromMessage:(NSString *)message inContext:(JSContext *)context;+ (JSValue *)valueWithNewPromiseInContext:(JSContext *)context fromExecutor:(void (^)(JSValue *resolve, JSValue *reject))callback API_AVAILABLE(macos(10.15), ios(13.0));+ (JSValue *)valueWithNewPromiseResolvedWithResult:(id)result inContext:(JSContext *)context API_AVAILABLE(macos(10.15), ios(13.0));+ (JSValue *)valueWithNewPromiseRejectedWithReason:(id)reason inContext:(JSContext *)context API_AVAILABLE(macos(10.15), ios(13.0));+ (JSValue *)valueWithNewSymbolFromDescription:(NSString *)description inContext:(JSContext *)context API_AVAILABLE(macos(10.15), ios(13.0));+ (JSValue *)valueWithNullInContext:(JSContext *)context;+ (JSValue *)valueWithUndefinedInContext:(JSContext *)context;- (id)toObject;- (id)toObjectOfClass:(Class)expectedClass;- (BOOL)toBool;- (double)toDouble;- (int32_t)toInt32;- (uint32_t)toUInt32;- (NSNumber *)toNumber;- (NSString *)toString;- (NSDate *)toDate;- (NSArray *)toArray;- (NSDictionary *)toDictionary;@property (readonly) BOOL isUndefined;@property (readonly) BOOL isNull;@property (readonly) BOOL isBoolean;@property (readonly) BOOL isNumber;@property (readonly) BOOL isString;@property (readonly) BOOL isObject;@property (readonly) BOOL isArray API_AVAILABLE(macos(10.11), ios(9.0));@property (readonly) BOOL isDate API_AVAILABLE(macos(10.11), ios(9.0));@property (readonly) BOOL isSymbol API_AVAILABLE(macos(10.15), ios(13.0));- (BOOL)isEqualToObject:(id)value;- (BOOL)isEqualWithTypeCoercionToObject:(id)value;- (BOOL)isInstanceOf:(id)value;// 当前 JSValue 为一个函数的时候,可以通过这个方法调用- (JSValue *)callWithArguments:(NSArray *)arguments;// 调用 JS 中的构造函数,arguments 数组内容必须是 JSValue 对象,以供 JS 能顺利转化- (JSValue *)constructWithArguments:(NSArray *)arguments;// 当前 JSValue 对象为 JS 中的全局对象名称,method 为全局对象的方法名称,arguments 为参数- (JSValue *)invokeMethod:(NSString *)method withArguments:(NSArray *)arguments;@end		

  由2.3.2章节中介绍我们可以得知,OC 层面的 Block 是可以自动转换为 JS 层面的函数,JS 可以直接访问;但是 JS 的函数 OC 确不能直接访问,而要通过 callWithArguments: 方法来调用。

  OC 的 id 类型传给 JS,只是一个指针,是没法访问其属性和方法的,但是 JS 回传到 OC 的时候 OC 还是可以正常访问的。如果需要在 JS 中,访问 OC 对象的属性和方法可以通过 JSExport 协议来实现,下面会详细介绍。

#pragma mark - JSValue- (void)ocExeJSFunction {    // 本地上下文环境//    JSContext *context = [[JSContext alloc] init];//    [context evaluateScript:@"function add(a, b) { return a + b; }"];//    JSValue *add = context[@"add"];//    NSLog(@"Func: %@", add);//    JSValue *sum = [add callWithArguments:@[@(18), @(32)]];//    NSLog(@"Sum: %d",[sum toInt32]);        //获取HTML上下文环境    JSContext *context = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];    JSValue *add = context[@"add"];    NSLog(@"Func: %@", add);    JSValue *sum = [add callWithArguments:@[@(18), @(32)]];    NSLog(@"Sum: %d",[sum toInt32]);}// OUTPUT//Func: function add(a, b) {//            return a + b;//       }//Sum: 50

2.5)JSExport

  实现 JSExport 协议可以开放 OC 类和它们的实例方法,类方法,以及属性给 JS 调用。我们先学习一个常规例子。

  1. 定义协议类:

#import <Foundation/Foundation.h>#import <JavaScriptCore/JavaScriptCore.h>NS_ASSUME_NONNULL_BEGIN@protocol JavaScriptExecuteOCDelegate <JSExport>// 属性@property(nonatomic,strong)NSString *propertyValue;// 无参数方法:调用OC的系统相册- (void)callSystemCamera;// 一个参数方法:通过JSON传值- (void)callWithDict:(NSDictionary *)params;// 多参数方法:在JS中调用时,函数名应该为showAlertMsg(arg1, arg2)- (void)showAlert:(NSString *)title msg:(NSString *)msg;// 回传实例:JS调用OC,然后在OC中通过调用JS方法来传值给JS。- (void)jsCallObjcAndObjcCallJsWithDict:(NSDictionary *)params;// 类方法+ (void)OCClassMethod;@endNS_ASSUME_NONNULL_END

  2. 创建模型类,遵守协议:

// 此模型用于注入JS的模型,这样就可以通过模型来调用方法。#import <Foundation/Foundation.h>#import <UIKit/UIKit.h>#import "JavaScriptExecuteOCDelegate.h"NS_ASSUME_NONNULL_BEGIN@interface HBNativeApisModel : NSObject<JavaScriptExecuteOCDelegate>@property(nonatomic, strong)JSContext *jsContext;@property(nonatomic, strong)UIWebView *webView;@endNS_ASSUME_NONNULL_END  #import "HBNativeApisModel.h"#import <JavaScriptCore/JavaScriptCore.h>@implementation HBNativeApisModel// 属性@synthesize propertyValue;// 无参数方法:调用OC的系统相册- (void)callSystemCamera {    NSLog(@"调用OC的系统相册方法");    JSValue *value = self.jsContext[@"cameraInfo"];    [value callWithArguments:nil];}// 一个参数方法:通过JSON传值- (void)callWithDict:(NSDictionary *)params {    NSLog(@"JS调用了OC的方法,参数为:%@", params);}// 多参数方法:在JS中调用时,函数名应该为showAlertMsg(arg1, arg2)- (void)showAlert:(NSString *)title msg:(NSString *)msg {    NSLog(@"JS调用了OC多参数方法:title=%@, msg=%@",title, msg);    dispatch_async(dispatch_get_main_queue(), ^{      UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:msg delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];      [alert show];    });}// 回传实例:JS调用OC,然后在OC中通过调用JS方法来传值给JS。- (void)jsCallObjcAndObjcCallJsWithDict:(NSDictionary *)params {    NSLog(@"JS调用了OC的方法,参数为:%@", params);    JSValue *value = self.jsContext[@"backParam"];    [value callWithArguments:@[@{@"name": @"Hubert", @"height": @175}]];}// 类方法+ (void)OCClassMethod {    NSLog(@"JS调用了OC的类方法");}@end

  3. 调用:

// 005 - 实现 JSExport 协议可以开放 OC 类和它们的实例方法,类方法,以及属性给 JS 调用- (void)jsExeNativeToJSExport {    HBNativeApisModel *model  = [[HBNativeApisModel alloc] init];    model.propertyValue = @"属性值:Hubert";        JSContext *context = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];    context[@"log"] = ^(NSString *msg){        NSLog(@"%@", msg);    };    model.jsContext = context;    model.webView = _webView;    // 注入实例    context[@"apiModel"] = model;    // 注入类    context[@"HBNativeApisModel"] = HBNativeApisModel.class;        context.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {      context.exception = exceptionValue;      NSLog(@"异常信息:%@", exceptionValue);    };    //    // 访问属性//    [context evaluateScript:@"log(apiModel.propertyValue)"];//    // 访问实例方法//    [context evaluateScript:@"apiModel.callSystemCamera()"];//    [context evaluateScript:@"apiModel.callWithDict({name:'Hubert'})"];//    [context evaluateScript:@"apiModel.jsCallObjcAndObjcCallJsWithDict({name:'Hubert'})"];//    // 访问实例方法 - 多个参数//    [context evaluateScript:@"apiModel.showAlertMsg('param1','param2')"];//    // 访问类方法//    [context evaluateScript:@"HBNativeApisModel.OCClassMethod()"];}

  4. 注意点:

  多个参数方法调用的时候,转换规则成驼峰形式,去掉所有的冒号,所有冒号后的第一个小写字母都会被转为大写。

  如果不喜欢默认的转换规则,也可以使用 JSExportAs(<#PropertyName#>, <#Selector#>) 来自定义转换,比如:

JSExportAs(callFun, - (void)callValue1:(NSString *)value1 value2:(NSString *)value2);)// 调用如下:[context evaluateScript:@"apiModel.callFun('param1','param2')"];

3. 利用WKWebView交互

3.1)了解WKWebView相关类

  • WKWebView:网页渲染与展示;
  • WKWebViewConfiguration:添加WebView配置信息;
  • WKUserScript:用于进行js注入;
  • WKUserContentController:这个类主要用来做native与JavaScript的交互管理;
  • WKScriptMessageHandler:这个协议类专门用来处理监听JavaScript方法从而调用原生OC方法,和WKUserContentController搭配使用;

3.2)了解WKWebView相关代理

  • WKNavigationDelegate :主要处理一些跳转、加载处理操作;
  • WKUIDelegate :主要处理JS脚本,确认框,警告框等;

3.3)h5调用原生

  这个实现主要是依靠WKScriptMessageHandler协议类和WKUserContentController两个类:WKUserContentController对象负责注册JS方法,设置处理接收JS方法的代理,代理遵守WKScriptMessageHandler协议,实现捕捉到JS消息的回调方法。

- (void)viewDidLoad {    [super viewDidLoad];    // Do any additional setup after loading the view.        // 创建网页配置对象的类    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];        // 这个类主要用来做native与JavaScript的交互管理    // 遵守WKScriptMessageHandler协议,代理是由WKUserContentControl设置    WKUserContentController *wkUController = [[WKUserContentController alloc] init];    [wkUController addScriptMessageHandler:self name:@"jsToOcWithPrams"];    config.userContentController = wkUController;        _webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds configuration:config];    _webView.navigationDelegate = self;    [self.view addSubview:_webView];        //打开URL    NSString *path = [[NSBundle mainBundle] pathForResource:@"web2" ofType:@"html"];    [_webView loadRequest:[NSURLRequest requestWithURL:[NSURL fileURLWithPath: path]]];}#pragma mark - WKScriptMessageHandler// 通过接收JS传出消息的name进行捕捉的回调方法  js调OC- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {    NSLog(@"name:%@ \n body:%@ \n frameInfo:%@",message.name,message.body,message.frameInfo);}

  输出结果:

name:jsToOcWithPrams body:{   age = "\U5e74\U9f84";   name = "\U540d\U5b57";} frameInfo:<WKFrameInfo: 0x7fed6452eb20; webView = 0x7fed64822000; isMainFrame = YES; request = <NSMutableURLRequest: 0x600002091660> { URL: file:///Users/Hubert/Library/Developer/CoreSimulator/Devices/872510EC-ECBA-42DE-BBF0-119826E07A79/data/Containers/Bundle/Application/9DEF6356-E45D-45BC-BBB5-057BDCC73237/BaseGrammar.app/web2.html }>

3.4)原生调用h5

  通过evaluateJavaScript: completionHandler:方法可以直接调用一段js代码。

- (void)changeBackgroundColor {    NSString *jsString = @"changeBackgroundColor()"; // 定义在js中的方法    [_webView evaluateJavaScript:jsString completionHandler:^(id _Nullable data, NSError * _Nullable error) {        NSLog(@"调用成功");    }];}

3.5)原生和h5协调

  h5调用原生:约定接口的规则,接口名和参数;

  原生调用h5:约定接口的规则,接口名和参数;

4. 利用WebViewJavascriptBridge开源库

  WebViewJavaScriptBridge 可以用于 WKWebView & UIWebView 中 OC 和 JS 交互。

  它的基本原理是:

  &emsp(1)把 OC 的方法注册到桥梁中,让 JS 去调用。

   (2)把 JS 的方法注册在桥梁中,让 OC 去调用。

iOS之H5与原生交互

4.1)WebViewJavaScriptBridge使用基本步骤

  1. 在项目里导入WebViewJavaScriptBridge框架;

  2. 导入头文件 #import <WebViewJavascriptBridge.h>

  3. 建立 WebViewJavaScriptBridge WebView 之间的关系;

    _bridge = [WebViewJavascriptBridge bridgeForWebView:_webView];
    
  4. 在HTML 文件中,复制粘贴这两段 JS 函数;

    /*这段代码是固定的,必须要放到js中*/function setupWebViewJavascriptBridge(callback) {  if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }  if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }  window.WVJBCallbacks = [callback];  var WVJBIframe = document.createElement('iframe');  WVJBIframe.style.display = 'none';  WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';  document.documentElement.appendChild(WVJBIframe);  setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)}    /*与OC交互的所有JS方法都要放在此处注册*/setupWebViewJavascriptBridge(function(bridge) {  }
    

4.2)往桥梁中注入 OC 方法 和 JS 函数

  1. 往桥梁中注入 OC 方法;

    - (void)registerHandlerForBridge {    // openCarama 是 OC block 的一个别名。    // block 本身,是 JS 通过某种方式调用到 openCarama 的时候,执行的代码块。    // data ,由于 OC 这端由 JS 调用,所以 data 是 JS 端传递过来的数据。    // responseCallback OC 端的 block 执行完毕之后,往 JS 端传递的数据。    [_bridge registerHandler:@"openCarama" handler:^(id data, WVJBResponseCallback responseCallback) {        NSLog(@"dataFrom JS : %@",data[@"data"]);        responseCallback(@"选择照片结果 : www.baidu.com");    }];}
    
  2. 往桥梁中注入 JS 函数;

    // 这里主要是注册 OC 将要调用的 JS 方法。setupWebViewJavascriptBridge(function(bridge){  	// testJavaScriptFunction 是注入到桥梁中 JS 函数的别名。以供 OC 端调用。		// 回调函数的 data。 既然 JS 函数由 OC 调用,所以 data 是 OC 端传递过来的数据。		// responseCallback 。 JS 调用在被 OC 调用完毕之后,向 OC 端传递的数据。   bridge.registerHanlder('testJavaScriptFunction',function(data,responseCallback){       // data 是 OC 传递过来的数据.       // responseCallback 是 JS 调用完毕之后传递给 OC 的数据       alert("JS 被 OC 调用了.");       responseCallback({data: "js 的数据",from : "JS"});   })});
    

    总结:

      OC 端注册 OC 的方法,OC 端调用 JS 的函数。
      JS 端注册 JS 的函数,JS 端调用 OC 的方法。

4.3)从桥梁中删除OC方法

  在当前控制器消失的时候,要记得把注入到桥梁中的 OC block,从桥梁中删除。否则,可能会出现控制器无法释放的情况。

- (void)dealloc {    [_bridge removeHandler:@"openCamera"];}
上一篇:h5页面在iphon11,12底部的小黑条


下一篇:H5页面调用移动端相机拍照