OC與JS交互

?一、簡(jiǎn)述

目前原生與JS交互的方式有以下幾種


JavaScriptCore

WKWebView

攔截URL

WebViewJavascriptBridge庫(kù)

二、JavaScriptCore


(一)定義

1.JSContext

為JS的執(zhí)行提供了上下文環(huán)境,通過(guò)JSCore執(zhí)行的JS代碼都要通過(guò)JSContext來(lái)執(zhí)行。(上下文對(duì)象給兩者的交互搭建了環(huán)境)

2.JSValue

是JS值在OC中的封裝徐鹤,以便JS值在OC中執(zhí)行

3.兩者關(guān)系

JSValue不可獨(dú)立存在配喳,必須依存于JSContext。一個(gè)JSContext可對(duì)應(yīng)多個(gè)JSValue

每一個(gè)JSValue對(duì)象都要強(qiáng)引用關(guān)聯(lián)一個(gè)JSContext凳干。當(dāng)與某JSContext對(duì)象關(guān)聯(lián)的所有JSValue釋放后晴裹,JSContext也會(huì)被釋放。?

(二)建立連接

1.文件頭部引入相關(guān)庫(kù)

#import <JavaScriptCore/JavaScriptCore.h>

2.加載本地HTML頁(yè)面

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"JSInteraction" ofType:@"html"];
??? NSString *htmlStr = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
??? [_webView loadHTMLString:htmlStr baseURL:[NSURL URLWithString:filePath]];

3.在webView的代理方法webViewDidFinishLoad中實(shí)現(xiàn)連接

//通過(guò)KVC方式從webView上獲取相應(yīng)的JSContext

self.context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]; ? ? ? ? ? ??

//設(shè)置錯(cuò)誤處理函數(shù)

[self.context setExceptionHandler:^(JSContext *context, JSValue *exception) {

NSLog(@"oc catches the exception: %@",exception); }];

self.context[@"JsInteractive"] = self;

Pay:由于加載JS在webViewDidFinishLoad()之前救赐,故JS無(wú)法調(diào)用OC方法涧团。

解決方法:

1)可在JS中構(gòu)建一個(gè)事件 “webViewDidFinishLoad()”,在原生的webVIewDidFinishLoad()方法中通過(guò)context的evaluateScript方法或者webView的stringByEvaluatingJavaScriptFromString()方法來(lái)調(diào)用经磅。

[self.context evaluateScript:@"webViewDidFinishLoad()"];

[webView stringByEvaluatingJavaScriptFromString:@"webViewDidFinishLoad()"];

2)通過(guò)JS延時(shí)操作setTimeout使得該方法在OC加載完畢后再執(zhí)行

setTimeout("showMessage('參數(shù)在此')",1000)

(三)交互方式

1.Block

1)JS調(diào)用OC

self.context[@"showMessage"] = ^(NSString *message){

?? NSLog(@"----showMessage-JS調(diào)用OC----參數(shù)為:%@",message); };

<button onclick="showMessage('我是參數(shù)')">showMessage-JS調(diào)用OC</button>

2)OC調(diào)用JS

?? //1.調(diào)用JS語(yǔ)句

? ? [self.context evaluateScript:@"alert('你好泌绣!');"];

? ? //2.調(diào)用JS中的變量

? ? JSValue *myValue = self.context[@"myObject"];

? ? NSLog(@"---%@---",[myValue toString]);

? ? //3.調(diào)用JS中的方法(以下兩種均可)

? ? [self.context evaluateScript:@"OcCallJs()"];

? ? [webView stringByEvaluatingJavaScriptFromString:@"OcCallJs()"];

? ? //調(diào)用有參數(shù)的方法

? ?? JSValue *news = self.context[@"showNews"];

?? ? [news callWithArguments:@[@"新聞?lì)^條",@"時(shí)事政治"]];

<script>

var myObject = "我的項(xiàng)目哈哈哈";

? ? ? ? ? ? function OcCallJs(){

? ? ? ? ? ? ? ? alert("我被OC調(diào)用了");

? ? ? ? ? ? }

??????????? function showNews(news,news2){
?????????????????? alert(news+news2);
?????? ? ?? }

</script>


2.Delegate交互


當(dāng)OC方法為多參數(shù)函數(shù)時(shí),其在JS中調(diào)用需要更改一下以適應(yīng)JS語(yǔ)法:

1)移除所有冒號(hào)

2)跟在冒號(hào)后面的參數(shù)的第一個(gè)字母大寫(xiě)

@protocol JSInteraction

-(void)showDelegateMessage:(NSString *)message;

-(void)showDelegateMessage:(NSString *)message andNumber:(NSInteger)number;

@end


-(void)showDelegateMessage:(NSString *)message{

? ? NSLog(@"我是被JS調(diào)用的OC的delegate方法预厌,參數(shù)為%@",message);

}

-(void)showDelegateMessage:(NSString *)message andNumber:(NSInteger)number{

NSLog(@"delegate多參數(shù)阿迈,參數(shù)為%ld--%@",number,message);

}

<script>

function webViewDidFinish(){

? ? ? ? ? ? ? ? JsInteractive.showDelegateMessage('參數(shù)');

???????????????? JsInteractive.showDelegateMessageAndNumber('123','1000000');

?}

</script>

OcCallJs()方法在上文提到的webVIewDidFinishLoad()中調(diào)用

三、WKWebView

(一)定義


1.WKWebView

兩種創(chuàng)建方式

//普通初始化

? ? self.wkWebView = [[WKWebView alloc]init];

? ? self.wkWebView.frame = self.view.bounds;

? ? //config方式初始化

? ? WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];

? ? self.wkWebView = [[WKWebView alloc]initWithFrame:self.view.bounds configuration:config];

? ? self.wkWebView.navigationDelegate = self;

? ? self.wkWebView.UIDelegate = self;

? ? [self.view addSubview:self.wkWebView];


加載網(wǎng)頁(yè)的方式

//加載本地URL文件

- (nullableWKNavigation*)loadFileURL:(NSURL*)URL allowingReadAccessToURL:(NSURL*)readAccessURL

//加載本地HTML字符串

- (nullableWKNavigation*)loadHTMLString:(NSString*)string baseURL:(nullableNSURL*)baseURL;

//加載二進(jìn)制數(shù)據(jù)

- (nullableWKNavigation*)loadData:(NSData*)data MIMEType:(NSString*)MIMEType characterEncodingName:(NSString*)characterEncodingName baseURL:(NSURL*)baseURL

2.WKWebViewConfiguration

如上代碼轧叽,webView初始化時(shí)配置webView的屬性苗沧,JS調(diào)用原生時(shí)需要用到。

3.WKUserContentController

這個(gè)類(lèi)主要用來(lái)負(fù)責(zé)原生與JS的交互管理

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

- (void)removeScriptMessageHandlerForName:(NSString *)name;

addScriptMessageHandler:name:方法注冊(cè)要被JS調(diào)用的方法的名稱(chēng)炭晒,然后在對(duì)應(yīng)的JavaScript中使用

window.webkit.messageHandlers.<name>.postMessage(<data>)

方法來(lái)向native發(fā)送消息待逞。最后記得將方法名remove掉,否則會(huì)造成內(nèi)存泄漏网严。

4.WKScriptMessageHandler

用來(lái)處理JS調(diào)用原生的方法

//接受JS發(fā)給原生的消息

- (void)userContentController:(WKUserContentController *)userContentControllerdidReceiveScriptMessage:(WKScriptMessage *)message;

5.WKUIDelegate

協(xié)議主要用于WKWebView處理web界面的三種提示框(警告框识樱、確認(rèn)框、輸入框)震束。提供用原生控件顯示網(wǎng)頁(yè)的方法回調(diào)怜庸。

(二)建立連接

1.文件頭部引入相關(guān)庫(kù)

#import <WebKit/WebKit.h>

2.加載HTML頁(yè)面

NSURL *urlString = [[NSBundle mainBundle] URLForResource:@"JSInteraction.html" withExtension:nil];

? ? if (urlString) {

? ? ? ? [_wkWebView loadRequest:[NSURLRequest requestWithURL:urlString]];

? ? }

WKUserContentController *userCC = config.userContentController;

(三)交互方式

1.原生調(diào)用JS


WKWebView 提供了一個(gè)新的方法evaluateJavaScript:completionHandler:,實(shí)現(xiàn)OC 調(diào)用JS 等場(chǎng)景垢村。

//獲取到input標(biāo)簽的value屬性值

NSString *inputValue = @"document.getElementsByName('input')[0].attributes['value'].value";
???
??? /** native 調(diào)用JS的方法 */
??? [webView evaluateJavaScript:inputValue completionHandler:^(id _Nullable response, NSError * _Nullable error) {
??????? NSLog(@"value:%@,error:%@",response,error);
??? }];

2.JS調(diào)用原生

/** JS調(diào)用OC? **/
??? //此處相當(dāng)于監(jiān)聽(tīng)了JS中showMobile這個(gè)方法
??? [self.userCC addScriptMessageHandler:self name:@"showMobile"];
??? [self.userCC addScriptMessageHandler:self name:@"showName"];

//移除WKUserContentController割疾,防止內(nèi)存泄漏
-(void)removeAllScriptHandler{
??? [self.userCC removeScriptMessageHandlerForName:@"showMobile"];
??? [self.userCC removeScriptMessageHandlerForName:@"showName"];
??? [self.userCC removeScriptMessageHandlerForName:@"showTwo"];
}

JS中:

?????? //多參數(shù)用數(shù)組傳遞
??????? function btnClick1() {??????????? window.webkit.messageHandlers.showMobile.postMessage(null)??????? }
??????? function btnClick2() {
??????????? window.webkit.messageHandlers.showName.postMessage('有個(gè)參數(shù)')
??????? }
??????? function? btnClick3() {
??????????? window.webkit.messageHandlers.showTwo.postMessage(['有兩個(gè)參數(shù)啦','canshu'])
??????? }

三、攔截URL

(一)定義

我們要攔截URL肝断,就要通過(guò)navigationDelegate的一個(gè)代理方法來(lái)實(shí)現(xiàn)杈曲。如果在HTML中要使用alert等彈窗,就必須得實(shí)現(xiàn)UIDelegate的相應(yīng)代理方法胸懈。

(二)建立連接

同WKWebView。


(三)交互方式

1.JS調(diào)用OC

2.OC 調(diào)用 JS

JS 調(diào)用OC 方法后恰响,有的操作可能需要將結(jié)果返回給JS趣钱。這時(shí)候就是OC 調(diào)用JS 方法的場(chǎng)景。

WKWebView 提供了一個(gè)新的方法evaluateJavaScript:completionHandler:胚宦,實(shí)現(xiàn)OC 調(diào)用JS 等場(chǎng)景首有。



使用自定義loadURL的原因:

如果當(dāng)前網(wǎng)頁(yè)正使用window.location.href加載網(wǎng)頁(yè)的同時(shí)燕垃,調(diào)用window.location.href去調(diào)用OC原生方法,會(huì)導(dǎo)致加載網(wǎng)頁(yè)的操作被取消掉井联。同樣的卜壕,如果連續(xù)使用window.location.href執(zhí)行兩次OC原生調(diào)用,也有可能導(dǎo)致第一次的操作被取消掉烙常。

自定義asyncAlert的原因

因?yàn)橛械腏S調(diào)用是需要OC 返回結(jié)果到JS的轴捎。stringByEvaluatingJavaScriptFromString是一個(gè)同步方法,會(huì)等待js 方法執(zhí)行完成蚕脏,而彈出的alert 也會(huì)阻塞界面等待用戶(hù)響應(yīng)侦副,所以他們可能會(huì)造成死鎖。導(dǎo)致alert 卡死界面驼鞭。如果回調(diào)的JS 是一個(gè)耗時(shí)的操作秦驯,那么建議將耗時(shí)的操作也放入setTimeout的function中。


3.同步和異步

因?yàn)?iOS SDK 沒(méi)有天生支持 js 和 native

相互調(diào)用挣棕,大家的技術(shù)方案都是自己實(shí)現(xiàn)的一套調(diào)用機(jī)制译隘,所以這里面有同步異步的問(wèn)題。細(xì)心的同學(xué)就能發(fā)現(xiàn)洛心,js 調(diào)用 native 是通過(guò)插入一個(gè)

iframe细燎,這個(gè) iframe 插入完了就完了,執(zhí)行的結(jié)果需要 native 另外用

stringByEvaluatingJavaScriptFromString 方法通知 js皂甘,所以這是一個(gè)異步的調(diào)用玻驻。

而 stringByEvaluatingJavaScriptFromString 方法本身會(huì)直接返回一個(gè) NSString 類(lèi)型的執(zhí)行結(jié)果,所以這顯然是一個(gè)同步調(diào)用偿枕。

所以 js call native 是異步璧瞬,native call js 是同步。

四渐夸、WebViewJavascriptBridge

1.實(shí)質(zhì):攔截URL來(lái)實(shí)現(xiàn)的調(diào)用原生功能

2.JS中實(shí)現(xiàn)的方法

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 = 'https://__bridge_loaded__';
??????????????? document.documentElement.appendChild(WVJBIframe);
??????????????? setTimeout(function() {
??????????????????? document.documentElement.removeChild(WVJBIframe)
???????????????????? }, 0)
??????????? }

這個(gè)方法的作用主要是在第一次加載HTML的時(shí)候起作用嗤锉,目的是加載一次wvjbscheme://__BRIDGE_LOADED__,來(lái)觸發(fā)往HTML中注入一些已經(jīng)寫(xiě)好的JS方法墓塌。

setupWebViewJavascriptBridge(function(bridge) {
?????????????? /* Initialize your app here */
?????????????? bridge.registerHandler('JS Echo', function(data, responseCallback) {
??????????????????????????????????????????????????????????? console.log("JS Echo called with:", data)
??????????????????????????????????????????????????????????? responseCallback(data)
??????????????????????????????????????????????????????????? })
??????????????? bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) {
??????????????????????????? console.log("JS received response:", responseData)
??????????????????????????????????????????????????????? })
??????????????????? })

Native 需要調(diào)用的 JS 功能瘟忱,也是需要先注冊(cè),然后再執(zhí)行的苫幢。如果Native 需要調(diào)用的JS 功能有多個(gè)访诱,那么這些功能都要在這里先注冊(cè),注冊(cè)之后才能夠被Native 調(diào)用韩肝。

參照:

http://www.reibang.com/p/7151987f012d

http://blog.devtang.com/2012/03/24/talk-about-uiwebview-and-phonegap/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末触菜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子哀峻,更是在濱河造成了極大的恐慌涡相,老刑警劉巖哲泊,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異催蝗,居然都是意外死亡切威,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)丙号,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)先朦,“玉大人,你說(shuō)我怎么就攤上這事槽袄±游蓿” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵遍尺,是天一觀的道長(zhǎng)截酷。 經(jīng)常有香客問(wèn)我,道長(zhǎng)乾戏,這世上最難降的妖魔是什么迂苛? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮鼓择,結(jié)果婚禮上三幻,老公的妹妹穿的比我還像新娘。我一直安慰自己呐能,他們只是感情好念搬,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著摆出,像睡著了一般朗徊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上偎漫,一...
    開(kāi)封第一講書(shū)人閱讀 52,441評(píng)論 1 310
  • 那天爷恳,我揣著相機(jī)與錄音,去河邊找鬼象踊。 笑死温亲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的杯矩。 我是一名探鬼主播栈虚,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼菊碟!你這毒婦竟也來(lái)了节芥?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤逆害,失蹤者是張志新(化名)和其女友劉穎头镊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體魄幕,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡相艇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纯陨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坛芽。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖翼抠,靈堂內(nèi)的尸體忽然破棺而出咙轩,到底是詐尸還是另有隱情,我是刑警寧澤阴颖,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布活喊,位于F島的核電站,受9級(jí)特大地震影響量愧,放射性物質(zhì)發(fā)生泄漏钾菊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一偎肃、第九天 我趴在偏房一處隱蔽的房頂上張望煞烫。 院中可真熱鬧,春花似錦累颂、人聲如沸滞详。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)料饥。三九已至,卻和暖如春瘦棋,著一層夾襖步出監(jiān)牢的瞬間稀火,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工赌朋, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凰狞,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓沛慢,卻偏偏與公主長(zhǎng)得像赡若,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子团甲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

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

  • 隨著H5技術(shù)的興起逾冬,在iOS開(kāi)發(fā)過(guò)程中,難免會(huì)遇到原生應(yīng)用需要和H5頁(yè)面交互的問(wèn)題。其中會(huì)涉及方法調(diào)用及參數(shù)傳值等...
    Chris_js閱讀 3,088評(píng)論 1 8
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類(lèi)型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,113評(píng)論 1 32
  • 前面講完攔截URL的方式實(shí)現(xiàn)JS與OC互相調(diào)用身腻,終于到JavaScriptCore了产还。它是從iOS7開(kāi)始加入的,用...
    咖啡綠茶1991閱讀 759評(píng)論 0 3
  • 注:JavaScriptCore API也可以用Swift來(lái)調(diào)用嘀趟,本文用Objective-C來(lái)介紹脐区。 在iOS7...
    JW_T閱讀 557評(píng)論 0 0
  • 想來(lái)法國(guó)很久了呢。 不知從何時(shí)起她按,我開(kāi)始向往那浪漫的都城牛隅,憧憬著在復(fù)古的街頭品一杯茶。 這...
    Daisy_柒小婷閱讀 143評(píng)論 0 0