好像是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.载入网页

  1. 可以直接导向到某个网页,也可以先在本地启动一个静态页面文件,后续一些工作可以在本地静态网页中用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);
  1. 把somepage.html添加到项目,并在项目设置中Build Phases->Copy Bundle Resources中添加上文件somepage.html,这样最后生成app文件的时候,somepage.html文件才会被打包到其中。
  2. 如果建立的项目使用沙箱(sandbox)模式,现在的应用,如果想上App Store,一般是强制要求使用沙箱的,需要在系统设置的Capabilities中允许incoming network/output networking。否则本地网页没问题,之后的任何网站都无法访问。
  3. 新版本的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.evaluateWebScript("callFromSwift('Hello, JavaScript')")
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='javascript:testCallSwift();'>testCallSwift</a><p>
<a href='javascript:needQuit();'>Quit</a><p>
<script>
function testCallSwift(){
//注意调用方式,window是js的对象
//swiftHost是swift的接口
//其后则是声明的swift函数
window.swiftHost.callFromJS1("hello swift");
}
function needQuit(){
window.swiftHost.quit();
}
</script>

5.截获webview每一次访问

跟上面类似,要再增加一个代理:

//初始化的时候增加:
webView!.policyDelegate=self;

并且声明类的时候多一个继承:WebPolicyDelegate。随后代码中可以实现一个接口:

    func webView(_ webView: WebView!,
decidePolicyForNavigationAction actionInformation: [AnyHashable : Any]!,
request: URLRequest!,
frame: WebFrame!,
decisionListener listener: WebPolicyDecisionListener!) {
NSLog("nav to %@",request.url!.absoluteString) //这里是将要转向的网址
listener.use() //允许访问这个网址
//listener.ignore() //不允许访问这个网址则调用这个
}

也有些程序中为了简化从js调用swift的工作量,会用链接的方式,在链接地址中传入一些指令,就可以用这个函数截获网址并且处理,被处理的网址通常使用listener.ignore()来禁止本次浏览器转向,免得影响当前页面。

6.响应js中的警告窗

通常的webview都是不允许js中的alert警告窗的,一方面是为了应用程序整体的效果;另一方面,webview作为一个空间,自己没有UI的控制权,所以类似的工作,是要有应用程序自己实现警告框窗口的。实现警告窗依然要给类增加一个集成WebUIDelegate,并在初始化中增加:

        webView!.uiDelegate=self;

//随后可以实现一个接口:
func webView(_ sender: WebView!,
runJavaScriptAlertPanelWithMessage message: String!,
initiatedBy frame: WebFrame!){
NSLog("msg of alert: %@",message)
}

如果不满足于只是得到警告消息,要自己在这个函数中使用cocoa的警告窗来显示相关的信息。

7.其它

还可以实现从js中访问swift中的变量功能。使用isKeyExcludedFromWebScriptwebScriptNameForKey函数,我用得少,如果需要,参考上面定义函数的方法,查一查官方文档自己来试试吧。

参考资料:

Swift & JavaScript integration

macOS webview编程的更多相关文章

  1. Android WebView编程的那些坑(一)

    最大的坑是ROM不同,webkit不同,差异性很大.再加上google的坑,真是坑上加坑.比如js注入问题,比如client回调接口时序问题, 比如内存回收问题,etc 1.内存泄漏问题,尤其注意An ...

  2. python scrapy 入门,10分钟完成一个爬虫

    在TensorFlow热起来之前,很多人学习python的原因是因为想写爬虫.的确,有着丰富第三方库的python很适合干这种工作. Scrapy是一个易学易用的爬虫框架,尽管因为互联网多变的复杂性仍 ...

  3. (转)Sencha Touch和jQuery Mobile的比较

    原文:http://extjs.org.cn/node/664 Sencha Touch和jQuery Mobile的比较 Posted 周三, 08/07/2013 - 10:07 by admin ...

  4. webView--总结

    Anaroid WebView API详解--http://blog.csdn.net/zhangcanyan/article/details/51344090;Android5.1系统WebView ...

  5. Sencha Touch 和 jQuery Mobile 的比较

    Sencha Touch 和 jQuery Mobile 的比较 英文原文:Sencha Touch vs jQuery Mobile 标签: Sencha Touch jQuery Mobile 1 ...

  6. Html5 移动应用软件开发框架 JqueryMobile SenchaTouch 介绍

    一.JqueryMobile 介绍 jQuery Mobile 是 jQuery 在手机上和平板设备上的版本. jQuery Mobile 不仅会给主流移动平台带来 jQuery 核心库,而且会发布一 ...

  7. 分享篇:聊一聊 15.5K 的 FileSaver,是如何工作的?

    聊一聊 15.5K 的 FileSaver,是如何工作的? FileSaver.js 是在客户端保存文件的解决方案,非常适合在客户端上生成文件的 Web 应用程序.它简单易用且兼容大多数浏览器,被作为 ...

  8. ASP.NET Core 新建项目 - macOS 环境 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 新建项目 - macOS 环境 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 新建项目 - macOS 环境 对于任何语言和 ...

  9. ASP.NET Core macOS 环境配置 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core macOS 环境配置 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 是对 ASP.NET 有重大意义的一次重新设计.本章节我 ...

随机推荐

  1. angularjs和ajax的结合使用 (三)

    转眼九月份了,忙忙碌碌 发现今年还没开过张,写一篇吧. 15年在空闲时就倒腾过angularjs那玩意儿 ,觉得还是挺好的,李金龙那厚厚的一本书,只不过没有系统化应用.最主要的是原来有一个东西没有用到 ...

  2. linux服务器用ssh 公钥下载代码 创建公钥,添加

    ssh-keygen -t rsa -C "8....@qq.com"cd ~/.sshcat id_rsa.pub 粘贴到对应的 https://github.com/setti ...

  3. VB生成条形码(EAN-13)

    14年给别人写的一个库存软件,用到扫码枪,所以就有了这个类. 编码规则相对简单,详见百度百科EAN-13 示例运行效果如下: 类模块:cEAN13.cls Option Explicit '★━┳━━ ...

  4. 在Linux上搭建测试环境常用命令(转自-测试小柚子)

    一.搭建测试环境: 二.查看应用日志: (1)vivi/vim 原本是指修改文件,同时可以使用vi 日志文件名,打开日志文件(2)lessless命令是查看日志最常用的命令.用法:less 日志文件名 ...

  5. MVC5 Razor视图中不规范书写导致的编译问题

    今天碰到一个非常让人难以理解的问题,如图所示,但是我在代码中并没有找到缺失"}"的地方: 根据源文件提示有去 AppData\Local\Temp\Temporary ASP.NE ...

  6. Android手机ROM刷机简介

    这一段时间刷了一堆ROM,安卓系列.CM.Hero.OldMos.隔壁新出的Android2.0,反正都尝了一下鲜,手机的照相机也刷得再也启不来了(不知道什么原因,杯具).有话说:"久病成良 ...

  7. Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件

    写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...

  8. Winform 窗体获得焦点

    给窗体添加Shown事件 public void Form_Shown(object sender, EventArgs e) { this.Activate(); this.Focus(); //定 ...

  9. DOS命令(二)

    1. findstr “要查找的字符串” 文件,用来从文件中检索包含相关内容的字符串集合. [例如:查找包含“TTL”的字符串] 2.  del 要删除的文件,用来删除某个文件. 3. pause,用 ...

  10. Ubuntu环境下配置darknet

    本教程基于Linux物理机进行相关配置,要求物理机中包含N卡且Capbility>=3.0,小于3.0(Fermi架构)只允许配置cuda,不能配置使用Cudnn: 本教程分为: 1.安装NVI ...