需求說明
目前的APP客戶端內,經常需要嵌入H5頁面進行混合開發。這樣,在開發過程中就會涉及到原生客戶端和H5交互的問題,本文就是簡單介紹一下iOS客戶端與H5頁面交互的一些方案,希望能解決大家的一些問題。
參考Demo:https://github.com/happyer-lwl/APP_Bridge_H5
一、Github上比較流行的是使用WebViewJavascriptBridge來實現交互
參考https://github.com/marcuswestin/WebViewJavascriptBridge
通過使用該庫可以輕松實現JS與原生交互。
// 初始化WebViewJavascriptBridge方法_bridge= [WebViewJavascriptBridge bridgeForWebView:self.webView webViewDelegate:selfhandler:^(iddata,WVJBResponseCallback responseCallback) {}];//原生與JS約定接口名為“testObjcCallback”,data是JS傳遞過來的信息,responseCallback來將信息傳遞給JS[_bridge registerHandler:@"testObjcCallback"handler:^(iddata,WVJBResponseCallback responseCallback) {? ? responseCallback("postInfomationToJS")}];
二、減少第三方依賴,使用iOS原生方法進行交互處理
1、OC調用JS
可以通過webView直接調用JS的方法或者獲取JS屬性
NSString*title = [self.webView stringByEvaluatingJavaScriptFromString:@"document.title"];
OC還可以通過webView的上下文,并執行JS腳本,為H5頁面動態添加方法和屬性,如下面代碼
// 獲取webView的上下文環境self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];// 為H5頁面動態添加屬性,并傳入APP端版本,之后H5可以使用此屬性[self.context evaluateScript:[NSStringstringWithFormat:@"var appVersion = %@",@"1.0.0"]];// 為H5頁面動態添加方法,并可以執行[self.context evaluateScript:@"var triple = function(value) { return value * 3 }"];JSValue *tripleNum = [context evaluateScript:@"triple(num)"];
OC調用JS方法
// 獲取webView的上下文環境self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];NSString* method =@"returnLogin";? ? JSValue * function = [self.context objectForKeyedSubscript:method];//這里面的a,b,c就是OC調用JS的時候給JS傳的參數[function callWithArguments:@[@"hahahahahahahahah"]];
2、JS調用OC方法,可以有返回值,分兩種情況:
①js直接調用方法,比較簡單
②js通過對象調用方法,需要用到JSExport進行一個綁定
一個協議如果遵從了JSExport協議,這個協議所規定的方法、變量等就會對js開放,我們可以通過js調用到
如果js是一個參數或者沒有參數的話 就比較簡單,我們的方法名和js的方法名保持一致即可
#import<UIKit/UIKit.h>#import<JavaScriptCore/JavaScriptCore.h>@protocolTestJSExport// callAppFunctionForJS 作為js方法的別名,可以相同JSExportAs (callAppFunction, - (void)callAppFunctionForJS:(NSString*)param);@end@interfaceViewController:UIViewController@property(weak,nonatomic)UIWebView*webView;@property(strong,nonatomic) JSContext *context;@end
#pragma mark - UIWebViewDelegate- (void)webViewDidFinishLoad:(UIWebView*)webView{// 以 html title 設置 導航欄 titleself.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];// 取得webView的運行上下文self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];// 打印異常self.context.exceptionHandler =? ? ^(JSContext *context, JSValue *exceptionValue)? ? {? ? ? ? context.exception = exceptionValue;NSLog(@"%@", exceptionValue);? ? };// 以JSExport協議關聯native的方法,JS通過對象調用native的方法需要加入此關鍵詞,如native.callAppFunctionForJS('param');self.context[@"native"] =self;// js調用iOS? // 第一種方案// 其中showAlert就是js的方法名稱,block 里面是OC代碼? // 此方法最終將打印出所有接收到的參數,js參數是不固定的 我們測試一下就知道? context[@"showAlert"] = ^() {NSArray*args = [JSContext currentArguments];for(idobjinargs) {NSLog(@"%@",obj);? ? ? ? ? }? ? ? }; }// js調用iOS? // 第二種方案// JS通過對象調用OC的方法// 可以有返回值給到JS- (NSString*)callAppFunctionForJS:(NSString*)param{NSLog(@"%@", param);}
2、小技術點
點擊導航欄返回,判斷APP原生返回還是H5頁面內部返回
/**
返回判斷,確認是否H5內部跳轉了
@param sender
*/- (void)backAction:(id)sender{// 獲取webView當前加載的頁面的數量,可以判斷是否在首頁,解決無法返回的問題NSString*webPageLength = [self.webView stringByEvaluatingJavaScriptFromString:@"history.length"];if([self.webView canGoBack] && (webPageLength.integerValue !=2)) {? ? ? ? [self.webView goBack];? ? }else{? ? ? ? [self.view resignFirstResponder];? ? ? ? [self.navigationController popViewControllerAnimated:YES];? ? }}
嵌入的H5頁面刷新或者跳轉之后,APP與H5的交互失效
原因
H5頁面刷新或者跳轉過后,原先的JSConext注入會消失,需要重新進行注入
解決
在-(void)webViewDidFinishLoad:(UIWebView *)webView中重新進行注入,切記:注入方法中的webView參數要用這個方法中帶的,不可使用自己類的webView屬性
- (void)webViewDidFinishLoad:(UIWebView*)webView{// webView不可用self.webView,要使用頁面刷新或者跳轉加載完成后的webViewself.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];}