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 的類型轉換:

image

  相關接口如下:

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 去調用。

image

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"];}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM