美文网首页iOS小模块
关于js和swift交互的理解(二)

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

作者: 4fdbb38ec548 | 来源:发表于2017-03-08 17:39 被阅读180次

本来打算第二天就写呢,结果一直忙到了周五,想想怕忘了写,于是挤了一点时间来把剩下的给大家补上,上篇文章介绍的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颗星!!!

相关文章

网友评论

    本文标题:关于js和swift交互的理解(二)

    本文链接:https://www.haomeiwen.com/subject/abzbgttx.html