好像是macOS10.10之后,以及iOS8之后,新出现的WKWebview组件就迅速的替代了Webview及UIWebView。后者的确存在一些无法解决的bug,诸如架构导致的速度缓慢和内存泄漏。
但无法避免的问题总是有的,比如有些客户端软件,仍然要求兼容老版本的系统,这时候,很不想使用,但也不得不仍然把Webview塞到自己的代码中。
互联网是个喜新厌旧的圈子,网上搜索,几乎只有两类。一是WKWebview的文档,二是iOS类的文档。想要的macOS下面Webview的资料缈如黄鹤。
经过部分只言片语的资料指导和大量的实验,终于完成了工作。所以决定来烧烧冷灶,写出来记录一下。
1.添加Webview
最简单添加webview的方法就是直接在Interface Builder中把Webview拖入到窗口并且用鼠标拖动到指定位置和指定大小,随后在程序中加上对应的变量:
@IBOutlet weak var webView: WebView!
如果必须动态程序实现,可以使用window.contentView?.addSubview(webView)
把webview控件插入到界面中。
2.载入网页
- 可以直接导向到某个网页,也可以先在本地启动一个静态页面文件,后续一些工作可以在本地静态网页中用js处理。这种方法是比较多用的,因为程序启动速度会感觉快的很多。
let path = Bundle.main.path(forResource: "somepage", ofType: "html")
let url = NSURL.fileURL(withPath: path!)
let request = URLRequest(url: url);
self.webView.mainFrame.load(request);
- 把somepage.html添加到项目,并在项目设置中Build Phases->Copy Bundle Resources中添加上文件somepage.html,这样最后生成app文件的时候,somepage.html文件才会被打包到其中。
- 如果建立的项目使用沙箱(sandbox)模式,现在的应用,如果想上App Store,一般是强制要求使用沙箱的,需要在系统设置的Capabilities中允许incoming network/output networking。否则本地网页没问题,之后的任何网站都无法访问。
- 新版本的macOS及iOS都强制必须使用https网页访问,如果需要支持老的http网页,还需要在Info.plist中增加一行:App Transport Security Settings,类型为字典项,其中增加一项:Allow Arbitrary Loads,值为YES。
完成以上4项,网页已经可以访问了。
3.从swift调用js
假定在网页中有如下内容:
<script>
function callFromSwift(msg){
document.getElementById('msgbox').innerHTML=msg;
return("msg return from js");
}
</script>
<div id='msgbox'></div>
其中定义了一个函数callFromSwift,当被调用的时候,在下面预定义的div中显示传入的字符串,并且返回一个字符串“msg return from js”。
在swift中调用网页中的callFromSwift函数并获取其返回值可以这样做:
let s=webView.windowScriptObject.eva luateWebScript("callFromSwift('Hello, java script')")
NSLog(s as! String) //s是js函数的返回结果,可以是多种类型,本例要求是string
4.从js调用swift
前面的3部分都比较容易,跟WKWebview也大同小异。从JS到swift的调用要复杂的多了。
首先在初始化的时候,要加上一句:
webView!.frameLoadDelegate=self;
对应的,要在类声明的位置加上一个继承:WebFrameLoadDelegate
,随后加入代码:
//为js对象声明一个接口
func webView(_ webView: WebView!, didClearWindowObject windowObject: WebScriptObject!, for frame: WebFrame!) {
self.webView.windowScriptObject.setValue(self, forKey: "swiftHost")
}
//这个是基本框架,声明了本类中有两个函数会开放给js对象,并供其调用
//这里示例了两个,一个是callFromJS1,另一个是quit
//注意swift中的函数名跟js中的函数名可以不一样,
//#selector中指明的是swift中声明的函数名,因为selector是object-c中的机制,
//所以后面在声明真正函数的时候,前面必须加@objc的标志
//在后面return "xxx"的部分,返回的字符串js中会使用的名字,
//本例中,swift中函数名跟js中函数名使用了相同的名字,我认为这是好习惯
override class func webScriptName(for aSelector: Selector) -> String?
{
//NSLog("%@",aSelector.description)
if aSelector == #selector(callFromJS1)
{
return "callFromJS1"
}
else
if aSelector == #selector(quit)
{
return "quit"
}
else
{
return nil
}
}
//这个函数顾名思义,应当是不允许在js中调用的,对所有的来值都返回false表示全部允许调用
override class func isSelectorExcluded(fromWebScript aSelector: Selector) -> Bool
{
//NSLog("%@",aSelector.description)
return false
}
//具体的函数
@objc
func callFromJS1(message:String)
{
NSLog(message)
}
@objc
func quit()
{
NSLog("call for quit")
NSApp.terminate(self);
}
前三个函数是基本的框架,其中第二个麻烦一些,随后实际上工作的函数没有什么特别。
接着来看看js的部分:
<a href='java script:testCallSwift();'>testCallSwift</a><p>