###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