UIWebView和WKWebView的使用及js交互

web页面和app直接的交互是很常见的东西,之前尝试过flex和js的相互调用以及android和js的相互调用,却只有ios没试过,据说比较复杂。周末花了点时间研究了一下,确实和其他的不太一样,但是 也不见复杂。

要知道的事情

ios的webview有2个类,一个叫UIWebView,另一个是WKWebView。两者的基础方法都差不多,本文重点是后者,他是取代UIWebView出现的,在app开发者若不需要兼容ios8之前版本,都应该使用WKWebVIew。

WKWebView 是苹果在 iOS 8 中引入的新组件,目的是给出一个新的高性能的 Web View 解决方案,摆脱过去 UIWebView 的老旧笨重特别是内存占用量巨大的问题,它使用Nitro JavaScript引擎,这意味着所有第三方浏览器运行JavaScript将会跟safari一样快.

ios9默认是不允许加载http请求的,对于webview,加载http网页也是不允许的。可以通过修改info.plist取消http限制

在项目中找到info.plist,源文件形式打开,添加下面内容


<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

dome截图

大纲

  • UIWebView使用

    • 加载网页或本地页面
    • app调js方法
    • js调app方法
  • WKWebView的使用
    • 加载页面,前进,后退,刷新,进度条
    • 前进,后退,刷新,进度条
    • js中alert的拦截
    • app调js方法
    • js调app方法
    • webView生命周期和跳转代理
  • web页面
  • 文章demo
  • 参考文章

UIWebView使用


UIVebView现在已经弃用,ios8以上都应该用新的WKWebview,所以UIWebView我就随意说说,大家随意看看。

加载网页或本地页面


//从本地加载html
let path:String! = NSBundle.mainBundle().pathForResource("index", ofType: "html")
webView.loadRequest(NSURLRequest(URL: NSURL.fileURLWithPath(path))) //从网络加载
webView.loadRequest(NSURLRequest(URL: NSURL(string: "https://www.bing.com")!))

注意点: 1. ios9默认不能加载http请求,需要声明 2. uiwebView网络请求会进入ternal func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool 委托,委托中若return false,也不会继续加载

app调js方法

app调用js方法使用的是 webView.stringByEvaluatingJavaScriptFromString() 这个方法。它可以直接执行一段js代码

    //调用js无参数的方法
webView.stringByEvaluatingJavaScriptFromString("hi()") //调用js有参数的方法hello(msg)
let js = String(format: "hello('%@')", "liuyanwei")
webView.stringByEvaluatingJavaScriptFromString(js) //调用js的参数为json对象
let js = String(format: "hello(%@)", "{'obj':'liuyanwei'}")
webView.stringByEvaluatingJavaScriptFromString(js) //从文件中加载一段js代码然后执行
do{
let jsString = try String(contentsOfFile: NSBundle.mainBundle().pathForResource("test", ofType: "js")!, encoding: NSUTF8StringEncoding)
self.webView.stringByEvaluatingJavaScriptFromString(jsString)
}
catch{} //直接执行alert
webView.stringByEvaluatingJavaScriptFromString("alert('hi')") //执行有返回值的js函数
NSLog("%@", webView.stringByEvaluatingJavaScriptFromString("getName()")!)

相关的js代码


var hi = function(){
alert("hello")
$(".info").html("hi");
} var hello = function(msg){
alert("hello " + msg)
if(msg.obj != undefined)
alert(msg.obj)
} var getName = function(){
return "liuyanwei"
}

js调app方法

很多人觉得,为什么UIWebView中,js调用app的方式怎么那么奇怪,其实应该这样说,UIWebView没有办法直接使用js调用app,但是可以通过拦截request的方式间接实现js调用app方法。

既然是拦截url,那你就可以任意方式去规定想要调用的url的路径和app中方法转换的方式。我这里使用协议和路径的方式,例如我拦截到的url是 “hello://hello_liuyanwei” ,我就把hello当做想调用的方法,路径当做参数。这种方式不一定好,但是使用起来还是挺方便的。

js中调用app的方法如下:


//这段代码是原生js代码,在js中的作用是做页面跳转
//webView通过拦截url请求方式拦截到request,通过解析从而调用 ios hello方法,参数是hello_liuyanwei
document.location = "hello://hello_liuyanwei";

//webView 需要实现UIWebViewDelegate委托方法
// class ViewController: UIViewController,UIWebViewDelegate ....
// webView.delegate = self func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool{ //如果请求协议是hello 这里的hello来自js的调用,在js中设为 document.location = "hello://liuyanwei 你好";
//scheme:hello ,msg:liuyanwei 你好
//通过url拦截的方式,作为对ios原生方法的呼叫
if request.URL?.scheme == "hello"{
let method:String = request.URL?.scheme as String!
let sel = Selector(method+":")
self.performSelector(sel, withObject:request.URL?.host)
request.URL?.path
//如果return true ,页面加载request,我们只是当做协议使用所以不能页面跳转
return false
} return true
}

最后说一下关于js调用app的返回值。app调js可以有返回值,但是js调app是通过间接的拦截request方式实现,它根本就不算方法调用,所以应该是不存在可以直接产生返回值的(如果不对欢迎指正)。当然如果需要app对js的调用有所响应,可以通过回叫函数的方式回应js。可以在调用app的时候增加一个js回叫函数名 app在处理完之后可以呼叫回叫函数并把需要的参数通过回叫函数的方式进行传递。

WKWebView的使用


WKWebVIew是UIWebView的代替品,新的WebKit框架把原来的功能拆分成许多小类。本例中主要用到了WKNavigationDelegate,WKUIDelegate,WKScriptMessageHandler三个委托和配置类WKWebViewConfiguration去实现webView的request控制,界面控制,js交互,alert重写等功能。 使用WKWebView需要引入#import <WebKit/WebKit.h>

加载页面,配置委托和手势等


//加载页面
config = WKWebViewConfiguration()
//设置位置和委托
webView = WKWebView(frame: self.webWrap.frame, configuration: config)
webView.navigationDelegate = self
webView.UIDelegate = self
self.webWrap.addSubview(webView) //加载网页
//webView.loadRequest(NSURLRequest(URL: NSURL(string: "https://www.bing.com")!)) //加载本地页面
webView.loadRequest(NSURLRequest(URL: NSURL.fileURLWithPath(NSBundle.mainBundle().pathForResource("index", ofType: "html")!)))
//允许手势,后退前进等操作
webView.allowsBackForwardNavigationGestures = true

前进,后退,刷新,进度条

//前进
webView.goBack()
//后退
webView.goForward()
//刷新
let request = NSURLRequest(URL:webView.URL!)
webView.loadRequest(request) //监听是否可以前进后退,修改btn.enable属性
webView.addObserver(self, forKeyPath: "loading", options: .New, context: nil)
//监听加载进度
webView.addObserver(self, forKeyPath: "estimatedProgress", options: .New, context: nil) //重写self的kvo方法
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if (keyPath == "loading") {
gobackBtn.enabled = webView.canGoBack
forwardBtn.enabled = webView.canGoForward
}
if (keyPath == "estimatedProgress") {
//progress是UIProgressView
progress.hidden = webView.estimatedProgress==1
progress.setProgress(Float(webView.estimatedProgress), animated: true)
}
}

js中alert的拦截

在WKWebview中,js的alert是不会出现任何内容的,你必须重写WKUIDelegate委托的runJavaScriptAlertPanelWithMessage message方法,自己处理alert。类似的还有Confirm和prompt也和alert类似,这里我只以alert为例。

    //alert捕获
func webView(webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: () -> Void) {
//
completionHandler()
let alert = UIAlertController(title: "ios-alert", message: "\(message)", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "ok", style: .Default, handler:nil))
alert.addAction(UIAlertAction(title: "cancel", style: .Cancel, handler: nil))
self.presentViewController(alert, animated: true, completion: nil) }

app调js方法

WKWebView调用js方法和UIWebView类似,一个是evaluateJavaScript,一个是stringByEvaluatingJavaScriptFromString。获取返回值的方式不同,WKWebView用的是回叫函数获取返回值


//直接调用js
webView.evaluateJavaScript("hi()", completionHandler: nil)
//调用js带参数
webView.evaluateJavaScript("hello('liuyanwei')", completionHandler: nil)
//调用js获取返回值
webView.evaluateJavaScript("getName()") { (any,error) -> Void in
NSLog("%@", any as! String)
}

js调app方法

UIwebView没有js调app的方法主要有2种实现,一种是通过拦截request的方式间接实现,另一种是使用JavaScriptCore的jsContext注册objc对象或使用JSExport协议导出Native对象的方式。本文主要介绍第一种实现,第二种实现方式参考后续文章 JavaScriptCore的使用教程

1:注册handler需要在webView初始化之前,如示例,注册了一个webViewApp的handler

        config = WKWebViewConfiguration()
//注册js方法
config.userContentController.addScriptMessageHandler(self, name: "webViewApp")
webView = WKWebView(frame: self.webWrap.frame, configuration: config)

2:处理handler委托。ViewController实现WKScriptMessageHandler委托的func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage)方法


//实现WKScriptMessageHandler委托
class ViewController:WKScriptMessageHandler //实现js调用ios的handle委托
func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
//接受传过来的消息从而决定app调用的方法
let dict = message.body as! Dictionary<String,String>
let method:String = dict["method"]!
let param1:String = dict["param1"]!
if method=="hello"{
hello(param1)
}
}

3:js调用

通过 window.webkit.messageHandlers.webViewApp找到之前注册的handler对象,然后调用postMessage方法把数据传到app,app通过上一步的方法解析方法名和参数


var message = {
'method' : 'hello',
'param1' : 'liuyanwei',
};
window.webkit.messageHandlers.webViewApp.postMessage(message);

如果需要app对js的调用有所响应,可以通过回叫函数的方式回应js。可以在调用app的时候增加一个js回叫函数名 app在处理完之后可以呼叫回叫函数并把需要的参数通过回叫函数的方式进行传递

webView生命周期和跳转代理

该代理提供的方法,可以用来追踪加载过程(页面开始加载、加载完成、加载失败)、决定是否执行跳转


// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation; // 接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
// 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

web页面


随便说两句web页面,demo中的web前段用了jquery去操作dom,btn是在js中添加的,btn的点击事件也在js中。另外随便写了几个css让页面稍微美观一些。web页面都在项目文件夹下的web文件夹中。大家其实也不必看,因为调用的app的js代码在文中都有单独贴出。

参考文章


本文也只是用了一些基本的用法,大家想了解更多,可以看看下面的三篇文章做补充阅读。但是现在也没发现有把webView这块写的很全很详细的文章,今后要是看见我会继续补充在这里。

demo


我博客中大部分示例代码都上传到了github,地址是:https://github.com/coolnameismy/demo,点击跳转代码下载地址

本文代码存放目录是ios-WebView,本demo没做界面自适应,为了保证效果请用iPhone6及以上模拟器打开

感谢收看,如果对大家有帮助,请github上follow和star,本文发布在刘彦玮的技术博客,转载请注明出处

dome截图

UIWebView和WKWebView的使用及js交互的更多相关文章

  1. iOS(UIWebView 和WKWebView)OC与JS交互 之二

    在iOS应用的开发过程中,我们经常会使用到WebView,当我们对WebView进行操作的时候,有时会需要进行源生的操作.那么我记下来就与大家分享一下OC与JS交互. 首先先说第一种方法,并没有牵扯O ...

  2. WKWebView新特性及JS交互

    引言 一直听说WKWebView比UIWebView强大许多,可是一直没有使用到,今天花了点时间看写了个例子,对其API的使用有所了解,为了日后能少走弯路,也为了让大家更容易学习上手,这里写下这篇文章 ...

  3. iOS(WKWebView)OC与JS交互 之三

      随着H5功能愈发的强大,没进行过混合开发的小伙们都不好意思说自己能够独立进行iOS的app开发,在iOS7操作系统下,常用的native,js交互框架有easy-js,WebViewJavascr ...

  4. WKWebView的使用与JS交互详细解读

    前言: WKWebView 这是在iOS8.0之后增加的一个比UIWebView更加完善和强大的控件!看网上关于它的博客也是有许多的了,从各个方面总结一下这个WKWebView看网上说它主要是为了和J ...

  5. 记录使用WKWebView进行OC与JS交互所踩过的坑

    目录: 1.页面cookie缓存 2.允许弹出JS的弹框 3.在webview页面加载的时候,添加加载进度条 4.禁止掉webview页面的长按复制粘贴功能 5.设置webview的userAgent ...

  6. ios WKWebView 与 JS 交互实战技巧

    一.WKWebView 由于Xcode8发布之后,编译器开始不支持iOS 7了,这样我们的app也改为最低支持iOS 8.0,既然需要与web交互,那自然也就选择使用了 iOS 8.0之后 才推出的新 ...

  7. OC与JS交互之UIWebView

    随着H5的强大,hybrid app已经成为当前互联网的大方向,单纯的native app和web app在某些方面显得就很劣势.关于H5的发展史,这里有一篇文章推荐给大家,今天我们来学习最基础的基于 ...

  8. WKWebView与JS交互,UIWebView+JavascriptCore和JS交互

    最近一直在做有关Swift和JavaScript交互的程序,所以有关UIWebView和WKWebView在使用上的差别在此总结下: UIWebView: (1)创建 var webView: UIW ...

  9. UIWebView 与 JS 交互(1):Objective-C 调用 Javascript

    众所周知,随着硬件水平的发展,HTML5 与原生 APP 性能差距不断缩小,正在互联网科技领域扮演者越来越重要的角色.作为一种能很大程度上节约成本的技术方案,通过 HTML5 及 JS 实现的跨平台技 ...

随机推荐

  1. SQL Server创建事务——锁

    参考地址:http://www.cnblogs.com/knowledgesea/p/3714417.html 事务定义: 事务是作为单个逻辑单元执行的一系列操作,它是一个不可分割的工作逻辑单元.它包 ...

  2. Python的socket网络编程(一)

    (注:本文部分内容摘自互联网,由于作者水平有限,不足之处,还望留言指正.) 先写首诗,抒抒情. 一. 食堂.校园 见过你那么多次 卑微的我 只敢偷偷瞄上一眼 心扑通 扑通 春天真好 不是么 二. 学子 ...

  3. Java Calendar类总结

    在实际项目当中,我们经常会涉及到对时间的处理,例如登陆网站,我们会看到网站首页显示XXX,欢迎您!今天是XXXX年....某些网站会记录下用户登陆的时间,比如银行的一些网站,对于这些经常需要处理的问题 ...

  4. git删除本地分支和删除远程分支

    引言: 切换分支的时候命令打错了,git checkout 后面没有跟分支名,结果git status,很多delete的文件,直接冒冷汗,git add ,commit 之后发现本地与远程确实是删除 ...

  5. SQL学习笔记之简易ORM

    0x00 前言 1 .我在实例化一个user对象的时候,可以user=User(name='lqz',password='123') 2 .也可以 user=User() user['name']=' ...

  6. SVN 的用法

    1.   SVN的服务端配置 接下来  我们要创建一个根目录(就是用于存诸工厂的),用来存诸SVN的工厂信息(每一个工厂等于一个项目)创建地址为:   d:/svn/ root(即在这里创建一个roo ...

  7. s3cmd安装

    配置yum.repos cd /etc/yum.repos.d/ vim s3tools.repo [s3tools] name=Tools for managing Amazon S3 - Simp ...

  8. 20145301《Java程序设计》实验报告一:Java开发环境的熟悉

    20145301<Java程序设计>实验报告一:Java开发环境的熟悉 课程:Java程序设计 实验名称:Java开发环境的熟悉 实验目的与要求: 1.没有Linux基础的同学建议先学习& ...

  9. 20145230熊佳炜《网络对抗》实验五:MSF基础应用

    20145230熊佳炜<网络对抗>实验五:MSF基础应用 主动攻击 首先,我们需要弄一个xp sp3 English系统的虚拟机,然后本次主动攻击就在我们kali和xp之间来完成. 然后我 ...

  10. 20145211《网络渗透》msf辅助模块的应用

    20145211<网络渗透>msf辅助模块的应用 一.实验准备 启用VB的kali,需要用到桥接,VMware桥接总是罢工…… 二.实验步骤 最好开桥接模式,要不然你就多开几个虚拟机(只要 ...