关于js和swift交互的理解(二)

本来打算第二天就写呢,结果一直忙到了周五,想想怕忘了写,于是挤了一点时间来把剩下的给大家补上,上篇文章介绍的swift调用js(文章地址 ),这篇文章介绍js调用swift

1.JS调用OC:webView拦截链接的方法

此方法本人并没有测试,是直接copy过来的,因为感觉此方法不是很好

-(BOOL)webView:(UIWebView *)webView

shouldStartLoadWithRequest:(NSURLRequest *)request

navigationType:(UIWebViewNavigationType)navigationType;

实现以上webView的代理方法,当webView每次开始加载URL时会进入这个方法,我们便可以在这个方法实现JS调用OC。

JS代码如下:

OC代码如下:

如上图当JS中window.location.href = "iOS:shareToTest"的代码被触发,会进入OC中的这个代理方法,并且获得"iOS:shareToTest"这个字符串,接下进行一系列的字符串解释,得到需要被实现的方法名且调用。如果需要传值可把需要传的值拼接在字符串上,字符串解释后获取响应的值后调用一下方法:

这种JS调用OC的方法的缺点十分明显,需要繁琐地解释字符串得到相应的方法名和传值,且最多只能有两个值,调用的方法也不能传递返回值;但是也有一个优点:不需要等待页面加载完才触发当相应的代码被运行就能调用OC的方法,这也是下面要讲到的JavaScriptCore的一个小坑。

2.苹果推荐的框架--JavaScriptCore

这种方法是利用iOS7后新出的框架实现的,跟我上文swift调用js 第二个方法是配套使用的,下面上代码:

首先创建一个类JSObjCModel和JavaScriptSwiftDelegate,代理里面写的是js可以调用的方法,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调用了我们的方法

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

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

}

// JS调用了我们的就去

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把刚刚创建的那个类注入到js里面,那么js就可以通过这个类去调用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调用我们公暴露的方法了。

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

self.jsContext?.exceptionHandler = {

(context, exception) in

print("exception @", exception!)

}

}

第三部js里面的写法是(如图,奇怪的是为何不能复制了,直接截图了),这个OCModel必须跟你的小伙伴商量好才可以,注意你在webViewDidFinishLoad里注入模型的时候写的必须一致才行



但是注意这个框架最需要强调的一点是:JS调用OC时,是需要等浏览器加载完页面后才能进行交互(相当坑、很坑!!!),这个是需要看需求的,如果你需要在网页加载的时候就调用,就放弃这个方吧,继续看下面的第三种办法

这个方法边写边发现了问题,问题如下调用2个参数时,怎么调用都不成功,如下图所有的地方都没错:



而js里的调用方法就是写的


大概经过半天的测试和调试,我终于发现了问题所在:

这就是咱们基础知识不扎实的地方了,还记得swift里的方法名是怎么定义的吗???

js里的方法应该写成什么就对了呢?

经过本大神的认真审查js里应该这么写navigateToCreateGroupBuyDataString才可以调到swift的方法,怎么样是不是想起了什么

3.优秀的第三方框架--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和发送一个call(图解)

handler注册

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

发送call

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


4) 把下述代码复制到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的开发人员外,也要让后台的人了解这个框架,并在合适的位置注入上述JS代码,虽然是比较麻烦,但是这个框架确实挺好用,推荐指数5颗星!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,881评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,052评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,598评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,407评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,823评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,872评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,037评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,778评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,505评论 1 247
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,745评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,233评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,231评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,141评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,939评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,954评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,784评论 2 275

推荐阅读更多精彩内容

  • 随着H5技术的兴起,在iOS开发过程中,难免会遇到原生应用需要和H5页面交互的问题。其中会涉及方法调用及参数传值等...
    Chris_js阅读 2,940评论 1 8
  • 最近整理了一下原生与H5之间的交互方式,简单的做个总结。OC端与JS的交互,大致有这几种:拦截协议、JavaScr...
    谈Xx阅读 30,998评论 41 75
  • 进入15年以后,在我们天朝越来越流行混编!尤其是腾讯的 变态APP微信小程序一出,撑起了混编的半边天! 废话不多说...
    白水灬煮一切阅读 1,190评论 0 2
  • 最近在做的项目重点就是原生app与js的交互,以前也做过但是并没有深入的了解和研究过,因为这个项目我尝试了三种方式...
    Www刘阅读 14,127评论 3 53
  • 1、这周工作上主要是涉及了两件事,一件是能否做到实时针对门的识别和标示,二是能否利用二维码判断出机器人的相对位姿。...
    吕鹏_hunhun阅读 223评论 0 2