iOS js調(diào)用原生的幾種實(shí)現(xiàn)方式

公司最近需求,有一些頁面使用了H5,主要場景是js要調(diào)用原生方法,同時(shí)原生把返回值傳個(gè)js

一、UIWebView 的 js原生交互

1、原生調(diào)用js

原生中主要代碼:

 聲明一個(gè)協(xié)議
@protocol JSObjctDeleagte <JSExport>
// iOSWebView對象調(diào)用的JavaScript方法,必須聲明?。?!
- (NSString *)jsCallOc:(NSString *)param;
@end

在webview加載完成時(shí)
- (void)webViewDidFinishLoad:(UIWebView *)webView {
    // 獲取webView的JSContext對象
    self.context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    
    //將iOSWebView對象指向自身
    //js 中加入 window.iOSWebView.jsCallOc 就會(huì)調(diào)用原生里的jsCallOc方法
    self.context[@"iOSWebView"] = self; //與js中保持一致
    self.context[@"console"][@"log"] = ^(JSValue * msg) {
        NSLog(@"H5  log : %@", msg);
    };
    
/**
 實(shí)現(xiàn)js需要調(diào)用的原生方法待js調(diào)用
 */
- (NSString *)jsCallOc:(NSString *)param {
    NSLog(@"我被js調(diào)用了,并接收到參數(shù)為:%@",param);
    return [NSString stringWithFormat:@"我被js調(diào)用了,js參數(shù)為:%@",param];
}

js中需要添加的代碼

//js調(diào)用oc方法
            function jsCallOc(param) {
                //iOSWebView js與原生約定好,保持一致
                var tempValue = window.iOSWebView.jsCallOc(param);//JS傳給oc的參數(shù)
                alert(tempValue);
            }

2、原生調(diào)用js

原生oc主要代碼:

- (void)buttonAction {    
    // oc調(diào)用js函數(shù) js無返回值
    NSString *jsAction = @"ocCallJs(11)";
    [self.context evaluateScript:jsAction];
    // oc調(diào)用js函數(shù) 傳參 并且有js返回值
    NSString *str1 = [self.webView stringByEvaluatingJavaScriptFromString:@"jsCallOc2(oc調(diào)用js函數(shù) 并傳參 接收js返回值);"];
    NSLog(@"js函數(shù)給我的返回值:%@", str1);
}

js中實(shí)現(xiàn)oc需要調(diào)用的方法

function jsCallOc2(param) {
            alert(param)
            return  '哈哈' + param
        }

到此一切看上去都很完美
但是在進(jìn)行調(diào)試的時(shí)候發(fā)現(xiàn),當(dāng)H5頁面內(nèi)部進(jìn)行頁面跳轉(zhuǎn)的時(shí)候,發(fā)現(xiàn)js 調(diào)用原生方法失效,找了網(wǎng)上的方法,很多都是說在- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType 代理方法中,remove掉之前的webview重新進(jìn)行創(chuàng)建,然而并沒有解決我的問題,也沒有找到其他方法,可能是我太菜??

然后就打算使用WKWebView替換UIWebView

好,接下來介紹基于WKWebView 的js原生交互
二、WKWebView 的 js原生交互
js 調(diào)用原生方法

原生中的代碼大概是這樣子的:

// js配置
    WKUserContentController *userContentController = [[WKUserContentController alloc] init];
    //需要JS調(diào)用iOS 原生的方法,都要添加到這里
    [userContentController addScriptMessageHandler:self name:@"finishHandle"];
    // WKWebView的配置
    WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
    configuration.userContentController = userContentController;
// 顯示W(wǎng)KWebView
    self.webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight-NavitionbarHeight) configuration:configuration];
    self.webView.UIDelegate = self; // 設(shè)置WKUIDelegate代理
    [self.view addSubview:self.webView];

然后js 中只需

window.webkit.messageHandlers.finishHandle.postMessage('哈哈');

window.webkit.messageHandlers是固定寫法


image.png

好像也挺簡單,但是這種方式原生沒有辦法把返回值傳給js
還有在使用這種方案,在頁面消失時(shí)需要removeScriptMessageHandlerForName,否則會(huì)造成循環(huán)引用

- (void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:animated];
 [self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"finishHandle"];
}

接上,下面介紹的這種方案有返回值,而且不會(huì)有重定向問題
實(shí)現(xiàn)的大致思路是,讓js調(diào)用prompt()方法,然后iOS 就會(huì)調(diào)用runJavaScriptTextInputPanelWithPrompt代理方法,在代理方法中攔截js參數(shù),再進(jìn)行判斷
原生代碼:
實(shí)現(xiàn)代理方法

//接收到輸入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {
    NSError *err = nil;
    NSData *dataFromString = [prompt dataUsingEncoding:NSUTF8StringEncoding];
    NSDictionary *payload = [NSJSONSerialization JSONObjectWithData:dataFromString options:NSJSONReadingMutableContainers error:&err];
    if (!err){
        NSString *type = [payload objectForKey:@"type"];
       //如果type == iOS 進(jìn)行處理,調(diào)用原生方法
        if (type && [type isEqualToString:@"iOS"]){
            completionHandler([self getReturnValueWithPayload:payload]);
            return;
        }
    }
   
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.text = defaultText;
    }];
    [alertController addAction:([UIAlertAction actionWithTitle:@"完成" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(alertController.textFields[0].text?:@"");
    }])];
    
    [self presentViewController:alertController animated:YES completion:nil];
}

- (void)configMethod:(NSDictionary *)payload {
    NSString *returnValue = @"";
    //JS調(diào)用原生方法名
    NSString *functionName = [payload objectForKey:@"functionName"];
   //JS傳入?yún)?shù)
    NSString *args = [payload objectForKey:@"arguments"]; // JS傳入的值
  //以下根據(jù)業(yè)務(wù)需要處理參數(shù),然后返回值給js
}

js代碼:

//從移動(dòng)端獲取數(shù)據(jù)
function  getAppData(functionName,param){
    var webView = isWebView();
    if(webView == "ios"){
        var payload = {"type": "iOS", "functionName": functionName, "arguments": param};
       //讓js調(diào)用prompt方法,傳入payload參數(shù),原生會(huì)接收到參數(shù)
      //functionName 需要調(diào)用的原生方法
        return prompt(JSON.stringify(payload));
    }else{
        throw new Error("環(huán)境異常!");
    }
}

以上方案大致符合公司需求,采用的就是這樣方案,但是這種方法有一個(gè)bug,就是在其他微信和支付寶中打開頁面會(huì)顯示彈窗

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容