###一、UIWebView的几种加载方式html
加载本地的HTML文件前端
//建立URL NSURL *url = [[NSBundle mainBundle] URLForResource:@"test" withExtension:@"html"]; //建立NSURLRequest NSURLRequest *request = [NSURLRequest requestWithURL:url]; //加载 [_webView loadRequest:request];
UIWebView 还支持将一个NSString对象做为源来加载。你能够为其提供一个基础URL,来指导UIWebView对象如何跟随连接和加载远程资源:java
NSString *path = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"]; NSString *htmlString = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; [webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:path]];
###二、UIWebView代理方法git
设置代理github
self.webView.delegate = self;
代理方法web
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType { //返回YES,进行加载。经过UIWebViewNavigationType能够获得请求发起的缘由 return YES; }
开始加载json
- (void)webViewDidStartLoad:(UIWebView *)webView { }
完成加载数组
- (void)webViewDidFinishLoad:(UIWebView *)webView { }
加载出错浏览器
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { }
###三、导航框架
UIWebView 类内部会管理浏览器的导航动做
[webView goBack]; //后退 [webView goForward]; //前进 [webView reload];//重载 [webView stopLoading];//取消载入内容
###四、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地址: