WKWebView的js交互(附demo)

前言

之前有寫過一篇是UIWebView的js交互担猛,但是UIWebView有內(nèi)存泄露問題,并且iOS13以后將不支持UIWebView瞒窒,所以現(xiàn)在大家一般都會(huì)使用WKWebView甥桂。
現(xiàn)在總結(jié)一下WKWebView加載網(wǎng)頁還有與JS交互的方法祷杈。

加載網(wǎng)頁分為加載本地html文件和加載線上的網(wǎng)頁鏈接摊鸡。為了方便講解津坑,我這里用的是加載本地html文件的方式妙蔗。

把一個(gè)html文件導(dǎo)入到工程中

圖片.png

導(dǎo)入webView框架

#import <WebKit/WebKit.h>

遵循代理

<WKUIDelegate,WKNavigationDelegate,WKScriptMessageHandler>

初始化webView 設(shè)置代理

    WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
    WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 64, self.view.frame.size.width, self.view.frame.size.height - 64) configuration:config];
    webView.navigationDelegate = self;
    webView.UIDelegate = self;

向網(wǎng)頁注入js

注入js分為在網(wǎng)頁document加載完畢注入和加載之前注入
// 在加載之前注入,這時(shí)注入的js會(huì)在網(wǎng)頁的所有元素加載之前和網(wǎng)頁本身的js執(zhí)行之前執(zhí)行
WKUserScriptInjectionTimeAtDocumentStart
// 在加載之后注入国瓮,當(dāng)網(wǎng)頁的元素加載之后以及網(wǎng)頁本身的js執(zhí)行之后才會(huì)執(zhí)行注入的js灭必。
WKUserScriptInjectionTimeAtDocumentEnd

    NSString *js = @"document.getElementsByTagName('h2')[0].innerText = '我是ios為h5注入的方法'";
    WKUserScript *script = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
    [config.userContentController addUserScript:script];

加載本地html文件

    NSString *filePath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
    NSString *htmlString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
    NSURL *url = [[NSURL alloc] initWithString:filePath];
    [self.webView loadHTMLString:htmlString baseURL:url];

WKNavigationDelegate代理方法實(shí)現(xiàn)

// 開始加載
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
    // 可以在這里做正在加載的提示動(dòng)畫 然后在加載完成代理方法里移除動(dòng)畫
}

// 網(wǎng)絡(luò)錯(cuò)誤
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
    // 在這里可以做錯(cuò)誤提示
}
    

// 網(wǎng)頁加載完成
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
     // 在這里可以移除正在加載的提示動(dòng)畫
}

WKUIDelegate代理方法實(shí)現(xiàn)

由于安全機(jī)制的問題,WKWebView默認(rèn)對(duì)JavaScript下alert類的方法(包括alert(),confirm(),prompt())做了攔截乃摹,如果要想正常使用禁漓,需要實(shí)現(xiàn)WKWebView的三個(gè)代理方法

// alert
//此方法作為js的alert方法接口的實(shí)現(xiàn),默認(rèn)彈出窗口應(yīng)該只有提示信息及一個(gè)確認(rèn)按鈕孵睬,當(dāng)然可以添加更多按鈕以及其他內(nèi)容播歼,但是并不會(huì)起到什么作用
//點(diǎn)擊確認(rèn)按鈕的相應(yīng)事件需要執(zhí)行completionHandler,這樣js才能繼續(xù)執(zhí)行
////參數(shù) message為  js 方法 alert(<message>) 中的<message>
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
    
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"確認(rèn)" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
}

// confirm
//作為js中confirm接口的實(shí)現(xiàn)掰读,需要有提示信息以及兩個(gè)相應(yīng)事件秘狞, 確認(rèn)及取消,并且在completionHandler中回傳相應(yīng)結(jié)果蹈集,確認(rèn)返回YES烁试, 取消返回NO
//參數(shù) message為  js 方法 confirm(<message>) 中的<message>
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
    
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }])];
    
    [alertController addAction:([UIAlertAction actionWithTitle:@"確認(rèn)" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }])];
    
    [self presentViewController:alertController animated:YES completion:nil];
}

// prompt
//作為js中prompt接口的實(shí)現(xiàn),默認(rèn)需要有一個(gè)輸入框一個(gè)按鈕拢肆,點(diǎn)擊確認(rèn)按鈕回傳輸入值
//當(dāng)然可以添加多個(gè)按鈕以及多個(gè)輸入框减响,不過completionHandler只有一個(gè)參數(shù),如果有多個(gè)輸入框郭怪,需要將多個(gè)輸入框中的值通過某種方式拼接成一個(gè)字符串回傳支示,js接收到之后再做處理
//參數(shù) prompt 為 prompt(<message>, <defaultValue>);中的<message>
//參數(shù)defaultText 為 prompt(<message>, <defaultValue>);中的 <defaultValue>
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
    
    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];
}

iOS調(diào)用js

下面的代碼是調(diào)用了js里的navButtonAction方法,并且傳入了兩個(gè)參數(shù)鄙才,回調(diào)里面response是這個(gè)方法return回來的數(shù)據(jù)颂鸿。

    [self.webView evaluateJavaScript:@"navButtonAction('Jonas',25)" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
        NSLog(@"%@=====%@",response,error);
    }];

js調(diào)iOS

當(dāng)JS端想傳一些數(shù)據(jù)給iOS.那它們會(huì)調(diào)用下方方法來發(fā)送。

window.webkit.messageHandlers.<方法名>.postMessage(<數(shù)據(jù)>)

注意:上方代碼在JS端寫會(huì)報(bào)錯(cuò),導(dǎo)致網(wǎng)頁后面業(yè)務(wù)不執(zhí)行.可使用try-catch執(zhí)行攒庵。

在web端具體代碼如下:

           try {
                // 注意這里就算不想傳參數(shù) 也要傳一個(gè)空字符串''過來 
                window.webkit.messageHandlers.show.postMessage('我是js傳遞過來的數(shù)據(jù)');
            } catch (e) {
                
            }

在OC中的處理方法如下嘴纺。它是WKScriptMessageHandler的代理方法name和上方JS中的方法名相對(duì)應(yīng)败晴。

- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;

具體代碼如下:

[webView.configuration.userContentController addScriptMessageHandler:self name:@"show"];

收到j(luò)s傳來的消息時(shí)會(huì)走WKScriptMessageHandler協(xié)議方法

- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
    NSLog(@"%@",message.name);// 方法名
    NSLog(@"%@",message.body);// 傳遞的數(shù)據(jù)
}

常見問題

1.控制器無法釋放問題

細(xì)心的朋友會(huì)發(fā)現(xiàn),加載WKWebView并添加js交互的控制器颖医,在退出后無法釋放掉位衩。
這是由于使用- (void)addScriptMessageHandler:(id <WKScriptMessageHandler>)scriptMessageHandler name:(NSString *)name;方法后,把self添加為handler會(huì)導(dǎo)致self被WKWebView強(qiáng)持有熔萧,造成循環(huán)引用糖驴。所以在使用這個(gè)方法時(shí)可以將它寫在控制器將出現(xiàn)時(shí)方法中,在控制器將消失方法中再移除佛致。

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.webView.configuration.userContentController addScriptMessageHandler:self name:@"name"];
}

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

2.app端調(diào)不到j(luò)s的方法

現(xiàn)在前端開發(fā)很多都不是原生開發(fā)贮缕,而是使用vue框架。而vue框架中俺榆,在methods:這個(gè)模塊里面聲明的方法感昼,不能注入到index.js文件里。原生調(diào)用js的方法卻只能在index.js里面找罐脊,所以前端開發(fā)人員定嗓,要么把方法在index.js里面聲明,要么在methods:模塊里聲明后萍桌,再在鉤子函數(shù)mounted里面用全局的window對(duì)象來引用方法宵溅。

mounted: function() {
  window.appMethod = this.appMethod;
},
methods:{
  appMethod: function (string) {
        console.log('app調(diào)用js方法參數(shù)=' + string);
  }
}

3.js調(diào)用OC后,OC如何返回?cái)?shù)據(jù)給js上炎?

js調(diào)用OC方法后恃逻,OC是沒法像安卓端那樣直接return數(shù)據(jù)給js的,只能通過OC調(diào)用js方法藕施,將數(shù)據(jù)發(fā)送過去寇损。

4.OC如何將字典發(fā)送給js?

將字典轉(zhuǎn)化為字符串,然后傳遞給js, js那邊收到后再解析為Json即可裳食。

    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
    NSString * str = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
    NSString *jsStr = [NSString stringWithFormat:@"getParamsFromNative(%@)", str];
    [self.webView evaluateJavaScript:jsStr
               completionHandler:^(id response, NSError * error) {
        NSLog(@"response: %@, \nerror: %@", response, error);
    }];

注意:如果將要傳遞的字典中某個(gè)值也為字典矛市,則需要將這個(gè)值的字典同樣轉(zhuǎn)化為字符串作為值才可以傳遞過去。

demo下載

最后為大家提供了一個(gè)簡單的demo诲祸,能夠更清晰的看到如何使用尘盼。
下載地址:
https://github.com/jiangbin1993/WKWebView-

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市烦绳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌配紫,老刑警劉巖径密,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異躺孝,居然都是意外死亡享扔,警方通過查閱死者的電腦和手機(jī)底桂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來惧眠,“玉大人籽懦,你說我怎么就攤上這事》湛” “怎么了暮顺?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長秀存。 經(jīng)常有香客問我捶码,道長,這世上最難降的妖魔是什么或链? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任惫恼,我火速辦了婚禮,結(jié)果婚禮上澳盐,老公的妹妹穿的比我還像新娘祈纯。我一直安慰自己,他們只是感情好叼耙,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布腕窥。 她就那樣靜靜地躺著,像睡著了一般旬蟋。 火紅的嫁衣襯著肌膚如雪油昂。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天倾贰,我揣著相機(jī)與錄音冕碟,去河邊找鬼。 笑死匆浙,一個(gè)胖子當(dāng)著我的面吹牛安寺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播首尼,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼挑庶,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了软能?” 一聲冷哼從身側(cè)響起迎捺,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎查排,沒想到半個(gè)月后凳枝,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年岖瑰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹋订。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡率挣,死狀恐怖椒功,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情玫锋,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布撩鹿,位于F島的核電站,受9級(jí)特大地震影響节沦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜甫贯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叫搁。 院中可真熱鬧,春花似錦渴逻、人聲如沸疾党。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至梨撞,卻和暖如春雹洗,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背卧波。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來泰國打工时肿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人港粱。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓嗜侮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子锈颗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容