背景:
最近帮公司VP孩子稍微指导了一下Swift及OS X程序,于是回忆之前自学过的Swift,回忆青春,回忆过去,觉的这些都不该被我们忘掉的东西就应该记录下来,多年后证明自己曾经无比热爱过。
契机:
我们现在项目都是JS编写内容,iOS只在学习的时候加载这一堆资源包,那么就产生了Swift(OC,暂时不列出来,用法基本一样)与JS通信的问题
1、JS调用Swift
2、Swift调用JS
知识点:
1、继承JSExport协议,增加和JS相关约定好的方法,如下:
import Foundation
import JavaScriptCore
@objc protocol ZBJSDelegate:JSExport {
///打印日志
func printJSLog(_ log: String)
///显示弹窗
func showAlert(_ msg: String)
///打开新页面
func openNewPage(_ url: String)
///待返回值的调用
func callAndReturn() -> Int
//js让我回调他
func callMe()
func callMe1()
}
2、再创建一个专门用于通信的类,继承协议,实现代理方法,如下:
代码如下
import UIKit
import JavaScriptCore
class ZBJSCallSwiftClass: NSObject, ZBJSDelegate {
var controller: UIViewController?
var jsContext: JSContext?
weak var delegate: ZBJSDelegate?
override init() {
super.init()
}
//实现jsDelegate的方法
func printJSLog(_ log: String) {
print(log)
if self.delegate != nil {
self.delegate!.printJSLog(log)
}
}
func showAlert(_ msg: String) {
print(msg)
}
func openNewPage(_ url: String) {
if self.delegate != nil {
self.delegate?.openNewPage(url)
}
}
func callAndReturn() -> Int {
return 666
}
func callMe() {
if self.delegate != nil {
self.delegate?.callMe()
}
}
func callMe1() {
self.jsContext?.evaluateScript("callBack('ZB真的好帅callMe1')")
}
}
3、这里就是你们需要加载webView的地方(业务层)
(1)这样分开的好处是,JS需要获取一些配置信息等等与业务无关的,可以在ZBJSCallSwiftClass中完成,与业务层相关的就放在业务层中处理。
(2)在webView的代理方法中webViewDidFinishLoad,先获取JSContext对象。
(3)JSContext对象表示JavaScript执行环境。您创建并使用JavaScript上下文来评估来自Objective-C或Swift代码的JavaScript脚本,访问在JavaScript中定义或计算的值,以及使JavaScript可以访问本机对象、方法或函数。
(4)JSValue实例是对JavaScript值的引用。您可以使用JSValue类在JavaScript和Objective-C或Swift表示之间转换基本值(比如数字和字符串),以便在本机代码和JavaScript代码之间传递数据。您还可以使用这个类来创建JavaScript对象,这些对象包装自定义类或JavaScript函数的本机对象,其实现由本机方法或块提供。
(5)JSValue(object:self.swiftObj, in:self.jsContext) 转换原生对象会创建一个JavaScript对象,包括一个构造函数和原型链,它反映了对象在Objective-C或Swift类型层次结构中的继承。默认情况下,转换对象上的属性和方法不会暴露给JavaScript:要选择哪些属性和方法应该对JavaScript可见,请参阅JSExport。创建包装本机对象的JSValue实例将保留底层Objective-C或Swift对象。
(6)JSManagedValue对象封装JSValue对象,添加“条件保留”行为来提供值的自动内存管理。托管值的主要用例是将JavaScript值存储在Objective-C或Swift对象中,该对象本身被导出到JavaScript。
(7)self.jsContext?.setObject(self.swiftObj, forKeyedSubscript:"swift"asNSCopying&NSObjectProtocol) 该方法首先从关键参数构造JSValue对象,然后在JavaScript中使用该值设置上下文的全局对象中的属性。使用此方法(或Objective-C下标语法)来桥接JavaScript中使用的本机对象或函数
self.swiftObj:要为JavaScript属性设置的值。
swift:要在上下文的全局JavaScript对象中使用的JavaScript属性名。
意思就是说,JS中可以直接swift.printJSLog('ZB666!')这么访问Swift方法了。
(8) 那么关键的是调用Swift call JS self.webView.stringByEvaluatingJavaScript(from: "callBack('ZB真是帅帅的(callMe)!')") 、self.jsContext?.evaluateScript("callBack('ZB真的好帅callMe1')")
代码如下:
import UIKit
import JavaScriptCore
class ZBWebViewController: UIViewController, UIWebViewDelegate, ZBJSDelegate {
func printJSLog(_ log: String) {
print("别慌,爸爸正在打印")
}
func callAndReturn() -> Int {
return 111
}
func callMe() {
DispatchQueue.main.async {
self.webView.stringByEvaluatingJavaScript(from: "callBack('ZB真是帅帅的(callMe)!')")
}
}
func callMe1() {}
let webView = UIWebView()
let width = UIScreen.main.bounds.size.width
let height = UIScreen.main.bounds.size.height
var jsContext: JSContext?
let swiftObj = ZBJSCallSwiftClass()
override func viewDidLoad() {
super.viewDidLoad()
self.title = "webView"
self.webView.frame = CGRect(x: 0, y: 0, width: width, height: height)
self.webView.backgroundColor = UIColor.gray
self.view.addSubview(self.webView)
// let path = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first! + "/test.html"
let path: String = Bundle.main.path(forResource: "test", ofType: "html")!
self.webView.delegate = self
let pathURL = NSURL.fileURL(withPath: path)
var content = ""
do {
content = try String.init(contentsOf: pathURL, encoding: String.Encoding.utf8)
} catch { }
self.webView.loadHTMLString(content, baseURL: pathURL)
self.swiftObj.delegate = self
}
func webViewDidStartLoad(_ webView: UIWebView) {
}
func webViewDidFinishLoad(_ webView: UIWebView) {
self.jsContext = webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as? JSContext
self.swiftObj.jsContext = self.jsContext
//遵循遵循一个retain机制
let jsObj = JSValue(object: self.swiftObj, in: self.jsContext)
let managedValue = JSManagedValue(value: jsObj)
self.jsContext?.virtualMachine.addManagedReference(managedValue, withOwner: self.swiftObj)
self.jsContext?.setObject(self.swiftObj, forKeyedSubscript: "swift" as NSCopying & NSObjectProtocol)
}
func webView(_ webView: UIWebView, didFailLoadWithError error: Error) {}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func showAlert(_ msg: String) {}
func openNewPage(_ url: String) {
DispatchQueue.main.async {
let webView = UIWebView()
webView.frame = CGRect(x: 0, y: 64, width: self.width, height: self.height-64)
self.view.addSubview(webView)
webView.loadRequest(URLRequest(url: URL(string: url)!))
}
}
}
4、JS调用Swift代码
代码如下
Document
js调用App的printJSLog
演示最基本的调用及参数传递
js调用App的打开新页面
演示打开新页面
js调用App的弹出对话框方法showAlert()
弹出框演示
js调用App的方法后 App再调用js函数执行回调
App调用js函数执行回调时 内容会改变
js调用App的方法后 App再调用js函数执行回调1
App调用js函数执行回调时 内容会改变1