關(guān)于js和swift交互的理解(二)

本來打算第二天就寫呢,結(jié)果一直忙到了周五,想想怕忘了寫,于是擠了一點時間來把剩下的給大家補上,上篇文章介紹的swift調(diào)用js(文章地址?),這篇文章介紹js調(diào)用swift

1.JS調(diào)用OC:webView攔截鏈接的方法

此方法本人并沒有測試,是直接copy過來的,因為感覺此方法不是很好

-(BOOL)webView:(UIWebView *)webView

shouldStartLoadWithRequest:(NSURLRequest *)request

navigationType:(UIWebViewNavigationType)navigationType;

實現(xiàn)以上webView的代理方法,當webView每次開始加載URL時會進入這個方法,我們便可以在這個方法實現(xiàn)JS調(diào)用OC。

JS代碼如下:

OC代碼如下:

如上圖當JS中window.location.href = "iOS:shareToTest"的代碼被觸發(fā),會進入OC中的這個代理方法,并且獲得"iOS:shareToTest"這個字符串,接下進行一系列的字符串解釋,得到需要被實現(xiàn)的方法名且調(diào)用。如果需要傳值可把需要傳的值拼接在字符串上,字符串解釋后獲取響應(yīng)的值后調(diào)用一下方法:

這種JS調(diào)用OC的方法的缺點十分明顯,需要繁瑣地解釋字符串得到相應(yīng)的方法名和傳值,且最多只能有兩個值,調(diào)用的方法也不能傳遞返回值;但是也有一個優(yōu)點:不需要等待頁面加載完才觸發(fā)當相應(yīng)的代碼被運行就能調(diào)用OC的方法,這也是下面要講到的JavaScriptCore的一個小坑。

2.蘋果推薦的框架--JavaScriptCore

這種方法是利用iOS7后新出的框架實現(xiàn)的,跟我上文swift調(diào)用js 第二個方法是配套使用的,下面上代碼:

首先創(chuàng)建一個類JSObjCModel和JavaScriptSwiftDelegate,代理里面寫的是js可以調(diào)用的方法,JSObjCModel這個名字需要跟前端的小伙伴一起約定好的,js里面也是要用的

import UIKit

import JavaScriptCore

// All methods that should apply in Javascript, should be in the following protocol.

@objc protocol JavaScriptSwiftDelegate: JSExport {

func callSystemCamera();

func showAlert(_ title: String, msg: String);

func callWithDict(_ dict: [String: AnyObject])

func jsCallObjcAndObjcCallJsWithDict(_ dict: [String: AnyObject]);

}

class JSObjCModel: NSObject,JavaScriptSwiftDelegate {

weak var controller: UIViewController?

weak var jsContext: JSContext?

func goGroup(_ commonId: String) {

print(commonId)

}

func callSystemCamera() {

print("js call objc method: callSystemCamera");

let jsFunc = self.jsContext?.objectForKeyedSubscript("jsFunc");

print(jsFunc?.toString()!)

jsFunc?.call(withArguments: []);

}

func showAlert(_ title: String, msg: String) {

DispatchQueue.main.async { () -> Void in

let alert = UIAlertController(title: title, message: msg, preferredStyle: .alert)

alert.addAction(UIAlertAction(title: "ok", style: .default, handler: nil))

self.controller?.present(alert, animated: true, completion: nil)

}

}

// JS調(diào)用了我們的方法

func callWithDict(_ dict: [String : AnyObject]) {

print("js call objc method: callWithDict, args: %@", dict)

}

// JS調(diào)用了我們的就去

func jsCallObjcAndObjcCallJsWithDict(_ dict: [String : AnyObject]) {

print("js call objc method: jsCallObjcAndObjcCallJsWithDict, args: %@", dict)

let jsParamFunc = self.jsContext?.objectForKeyedSubscript("jsParamFunc");

let dict = NSDictionary(dictionary: ["age": 18, "height": 168, "name": "lili"])

jsParamFunc?.call(withArguments: [dict])

}

}

然后就需要在webViewDidFinishLoad把剛剛創(chuàng)建的那個類注入到j(luò)s里面,那么js就可以通過這個類去調(diào)用swift里的方法了

func webViewDidFinishLoad(_ webView: UIWebView) {

hideActivity()

//刪除頭部試圖

let header = "document.getElementById('header').remove()"

webView.stringByEvaluatingJavaScript(from: header)

self.title = webView.stringByEvaluatingJavaScript(from: "document.title")

let context = webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as? JSContext

let model = JSObjCModel()

model.controller = self

model.jsContext = context

self.jsContext = context

// 這一步是將OCModel這個模型注入到JS中,在JS就可以通過OCModel調(diào)用我們公暴露的方法了。

self.jsContext?.setObject(model, forKeyedSubscript: "OCModel" as (NSCopying & NSObjectProtocol)!)

self.jsContext?.exceptionHandler = {

(context, exception) in

print("exception @", exception!)

}

}

第三部js里面的寫法是(如圖,奇怪的是為何不能復(fù)制了,直接截圖了),這個OCModel必須跟你的小伙伴商量好才可以,注意你在webViewDidFinishLoad里注入模型的時候?qū)懙谋仨氁恢虏判?/p>



但是注意這個框架最需要強調(diào)的一點是:JS調(diào)用OC時,是需要等瀏覽器加載完頁面后才能進行交互(相當坑、很坑!!!),這個是需要看需求的,如果你需要在網(wǎng)頁加載的時候就調(diào)用,就放棄這個方吧,繼續(xù)看下面的第三種辦法

這個方法邊寫邊發(fā)現(xiàn)了問題,問題如下調(diào)用2個參數(shù)時,怎么調(diào)用都不成功,如下圖所有的地方都沒錯:



而js里的調(diào)用方法就是寫的


大概經(jīng)過半天的測試和調(diào)試,我終于發(fā)現(xiàn)了問題所在:

這就是咱們基礎(chǔ)知識不扎實的地方了,還記得swift里的方法名是怎么定義的嗎???

js里的方法應(yīng)該寫成什么就對了呢?

經(jīng)過本大神的認真審查js里應(yīng)該這么寫navigateToCreateGroupBuyDataString才可以調(diào)到swift的方法,怎么樣是不是想起了什么

3.優(yōu)秀的第三方框架--WebViewJavascriptBridge

由于我利用第二個就解決了需求,但是我還是感覺第三種方法最好,目前這個庫還在更新中,我也沒有用我的swift項目中,但是目測這是最好的解決方法,寫到這里我還是忍不住,相對它嘗試一下

先奉上這個框架的GitHub地址WebViewJavascriptBridge

具體用法在GitHub上說的挺詳細的,下面大概說一下吧:

1) 首先把第三方加入你的項目并引用文件

#import"WebViewJavascriptBridge.h"

...

@property WebViewJavascriptBridge* bridge;

2) 注冊一個WebViewJavascriptBridge的對象 可以用 WKWebView, UIWebView (iOS) or WebView (OSX):

self.bridge = [WebViewJavascriptBridgebridgeForWebView:webView];

3) oc里面注冊一個Handler和發(fā)送一個call(圖解)

handler注冊

[self.bridgeregisterHandler:@"ObjC Echo"handler:^(iddata, WVJBResponseCallback responseCallback) {NSLog(@"ObjC Echo called with:%@", data);responseCallback(data);}];

發(fā)送call

[self.bridgecallHandler:@"JS Echo"data:nilresponseCallback:^(idresponseData) {NSLog(@"ObjC received response:%@", responseData);}];


4) 把下述代碼復(fù)制到JS

functionsetupWebViewJavascriptBridge(callback) {if(window.WebViewJavascriptBridge) {returncallback(WebViewJavascriptBridge); }if(window.WVJBCallbacks) {returnwindow.WVJBCallbacks.push(callback); }window.WVJBCallbacks=[callback];varWVJBIframe=document.createElement('iframe');WVJBIframe.style.display='none';WVJBIframe.src='https://__bridge_loaded__';document.documentElement.appendChild(WVJBIframe);setTimeout(function() {document.documentElement.removeChild(WVJBIframe) },0)}


5)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'},functionresponseCallback(responseData) {console.log("JS received response:", responseData)? ? })})

如果真的要用到這個框架,除了iOS的開發(fā)人員外,也要讓后臺的人了解這個框架,并在合適的位置注入上述JS代碼,雖然是比較麻煩,但是這個框架確實挺好用,推薦指數(shù)5顆星!!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

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

  • 隨著H5技術(shù)的興起,在iOS開發(fā)過程中,難免會遇到原生應(yīng)用需要和H5頁面交互的問題。其中會涉及方法調(diào)用及參數(shù)傳值等...
    Chris_js閱讀 3,109評論 1 8
  • 最近整理了一下原生與H5之間的交互方式,簡單的做個總結(jié)。OC端與JS的交互,大致有這幾種:攔截協(xié)議、JavaScr...
    談Xx閱讀 31,163評論 41 75
  • 進入15年以后,在我們天朝越來越流行混編!尤其是騰訊的 變態(tài)APP微信小程序一出,撐起了混編的半邊天! 廢話不多說...
    白水灬煮一切閱讀 1,256評論 0 2
  • 最近在做的項目重點就是原生app與js的交互,以前也做過但是并沒有深入的了解和研究過,因為這個項目我嘗試了三種方式...
    Www劉閱讀 14,257評論 3 53
  • 1、這周工作上主要是涉及了兩件事,一件是能否做到實時針對門的識別和標示,二是能否利用二維碼判斷出機器人的相對位姿。...
    呂鵬_hunhun閱讀 254評論 0 2