您的位置:首页 > 移动开发

IOS——UIWebView基本使用与JS交互

2016-09-23 00:00 417 查看
摘要: UIWebView是iOS sdk中一个最常用的控件,是内置的浏览器控件,我们可以用它来浏览网页、打开文档等等。

###1、UIWebView的几种加载方式

加载本地的HTML文件

通过NSURLRequest加载:

//创建URL
NSURL *url = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"html"];
//创建NSURLRequest
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//加载
[_webView loadRequest:request];


通过NSString加载:

UIWebView 还支持将一个NSString对象作为源来加载。你可以为其提供一个基础URL,来指导UIWebView对象如何跟随链接和加载远程资源:

NSString *path = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
NSString *htmlString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:path]];

###2、UIWebView代理方法

设置代理

self.webView.delegate = self;

代理方法

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
//返回YES,进行加载。通过UIWebViewNavigationType可以得到请求发起的原因
return YES;
}

开始加载

- (void)webViewDidStartLoad:(UIWebView *)webView
{

}

完成加载

- (void)webViewDidFinishLoad:(UIWebView *)webView
{

}

加载出错

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{

}

###3、导航

UIWebView 类内部会管理浏览器的导航动作

[webView goBack]; //后退
[webView goForward]; //前进
[webView reload];//重载
[webView stopLoading];//取消载入内容

###4、UIWebView和JavaScript交互

UIWebView的方法

- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;

相关应用:

// 获取当前页面的title
NSString *title = [webview stringByEvaluatingJavaScriptFromString:@"document.title"];

// 获取当前页面的url
NSString *url = [webview stringByEvaluatingJavaScriptFromString:@"document.location.href"];

iOS原生应用和web页面的交互大致上有这几种方法iOS7之后的
JavaScriptCore
、拦截协议、第三方框架
WebViewJavaScriptBridge
、iOS8之后的
WKWebView

在这里只介绍
JavaScriptCore
和'拦截协议'

iOS7之后苹果推出了JavaScriptCore这个框架,从而让web页面和本地原生应用交互起来非常方便,而且使用此框架可以做到Android那边和iOS相对统一,web前端写一套代码就可以适配客户端的两个平台,从而减少了web前端的工作量。

####4.1 JavaScriptCore

JavaScriptCore中类及协议:

JSContext
:给JavaScript提供运行的上下文环境
JSValue
:JavaScript和Objective-C数据和方法的桥梁
JSManagedValue
:管理数据和方法的类
JSVirtualMachine
:处理线程相关,使用较少
JSExport
:这是一个协议,如果采用协议的方法交互,自己定义的协议必须遵守此协议

web前端

在三端交互中,web前端要强势一些,一切传值、方法命名都按web前端开发人员来定义,让另外两端去做适配。在这里以调用NSLog和保存图片为例来详细讲解,测试网页代码取名为test.html,其代码内容如下:

test.html代码内容

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
</head>
<body>
<div style="text-align: center;">
<h1>Objective-C和JavaScript交互的那些事</h1>
<div><img src="http://chuantu.biz/t5/34/1474337388x3063167327.jpg" /></div>
<div><input type="button" value="点击我可以看到Xcode的NSLog" onclick="Toyun.callCamera()" /></div>
<div><input type="button" value="点击我可以保持图片到手机相册" onclick="callShare()" /></div>
</div>
<script>
var callShare = function() {
var shareInfo = JSON.stringify({"title": "标题", "desc": "内容", "shareUrl": "http://chuantu.biz/t5/34/1474337388x3063167327.jpg"});
Toyun.share(shareInfo);
}
var picCallback = function(photos) {
alert(photos);
}
var shareCallback = function(NSString){
alert(NSString);
}

</script>
</body>
</html>

test.html代码解释:

可能有些同学对web前端的一些知识不太熟悉,稍微对这段代码做下解释,先说Toyun是iOS和Android这两边在本地要注入的一个对象【参考下面iOS的代码更容易明白】,充当原生应用和web页面之间的一个桥梁。页面上定义了两个按钮名字分别为
点击我可以看到Xcode的NSLog
点击我可以保持图片到手机相册
。点击第一个按钮会通过Toyun这个桥梁调用本地应用的方法- (void)callCamera,没有传参;而点击第二个按钮会先调用本文件中的JavaScript方法callShare,这里将要保存的内容格式转成JSON字符串格式(这样做是为了适配Android,iOS可以直接接受JSON对象)然后再通过Toyun这个桥梁去调用原生应用的- (void)share:(NSString *)shareInfo方法这个是有传参的,参数为shareInfo。而下面的两个方法为原生方法调用后的回调方法,其中picCallback为获取图片成功的回调方法,并且传回拿到的参数photos;shareCallback为分享成功的回调方法。

IOS代码

通过JSContext,在ObjC中通过JSContext注入模型,然后调用模型的方法:

首先,我们需要先定义一个协议,而且这个协议必须要遵守JSExport协议

@protocol JavaScriptObjectiveCDelegate <JSExport>
//协议方法
- (void)callCamera ;
- (void)share:(id)shareString ;
@end

接下来,我们还需要定义一个模型,这个模型遵循我们定义的协议

// 此模型用于注入JS的模型,这样就可以通过模型来调用方法.
@interface Model : NSObject<JavaScriptObjectiveCDelegate>

实现协议方法:

- (void)callCamera {

NSLog(@"点击了网页按钮");

JSValue *picCallback = self.jsContext[@"picCallback"];
//参数传递
[picCallback callWithArguments:@[@"请看Xcode!"]];
}

- (void)share:(id)shareString {

NSLog(@"share:%@", shareString);

NSData *json = [shareString dataUsingEncoding:NSUTF8StringEncoding];

// 将json格式数据解析为对应的数据类型(数组、字典、字符串、数字)
NSError *error = nil;
// NSJSONReadingMutableContainers: 转为对应的可变容器(可变数组、可变字典)
NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:json options:NSJSONReadingMutableContainers error:&error];

NSLog(@"%@",jsonData[@"shareUrl"]);

NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:jsonData[@"shareUrl"]]];

UIImage *image = [UIImage imageWithData:data];

// 将图片保存到相册中

UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL);

}

接下来,我们在controller中在webview加载完成的代理中,给JS注入模型

- (void)webViewDidFinishLoad:(UIWebView *)webView {
self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
// 通过模型调用方法,这种方式更好些。
Model *model  = [[Model alloc] init];
self.jsContext[@"Toyun"] = model;
model.jsContext = self.jsContext;
model.webView = self.webView;

self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exceptionValue) {
context.exception = exceptionValue;
NSLog(@"异常信息:%@", exceptionValue);
};
}

####4.2 拦截协议

拦截协议这个适合一些比较简单的一些情况,不需要引入什么框架,只需要web前端配合一下就好。但是在具体调用哪一个方法上,以及在传值的时候可能会有些不方便,而且调用完后无法在回调JavaScript的方法。

test.html中的代码

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
</head>
<body>
<div>
<input type="button" value="CallCamera" onclick="callCamera()">
</div>

<script>
function callCamera() {
window.location.href = 'toyun://callCamera';
}
</script>
</body>
</html>

test.html中的代码解释:

这段代码相比上面的那段测试代码是很简单的,同样有一个按钮,名字为CallCamera点击之后调用自己的callCamera方法,window.location.href这里是改变主窗口的指向从而马上发出一个链接为toyun://callCamera请求,而想要传给原生应用的参数也可已包含到此请求中,而在iOS方法中我们要拦截这个请求,根据请求内容去判断JavaScript想要做的事情,从而实现web页面和本地应用之间的交互。

iOS对应的代码

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString *url = request.URL.absoluteString;
if ([url rangeOfString:@"toyun://"].location != NSNotFound) {
// url的协议头是toyun
NSLog(@"callCamera");
return NO;
}
return YES;
}

iOS对应的代码的解释:

在webView的代理方法中去拦截自定义的协议Toyun://如果是此协议则据此判断JavaScript想要做的事情,调用原生应用的方法,这些都是提前约定好的,同时阻止此链接的跳转。

Demo地址:

https://github.com/fuxinto/HfxDemo
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息