IOS——UIWebView基本使用与JS交互
2016-09-23 00:00
417 查看
摘要: UIWebView是iOS sdk中一个最常用的控件,是内置的浏览器控件,我们可以用它来浏览网页、打开文档等等。
###1、UIWebView的几种加载方式
加载本地的HTML文件
通过NSURLRequest加载:
通过NSString加载:
UIWebView 还支持将一个NSString对象作为源来加载。你可以为其提供一个基础URL,来指导UIWebView对象如何跟随链接和加载远程资源:
###2、UIWebView代理方法
设置代理
代理方法
开始加载
完成加载
加载出错
###3、导航
UIWebView 类内部会管理浏览器的导航动作
###4、UIWebView和JavaScript交互
UIWebView的方法
相关应用:
iOS原生应用和web页面的交互大致上有这几种方法iOS7之后的
在这里只介绍
iOS7之后苹果推出了JavaScriptCore这个框架,从而让web页面和本地原生应用交互起来非常方便,而且使用此框架可以做到Android那边和iOS相对统一,web前端写一套代码就可以适配客户端的两个平台,从而减少了web前端的工作量。
####4.1 JavaScriptCore
JavaScriptCore中类及协议:
web前端
在三端交互中,web前端要强势一些,一切传值、方法命名都按web前端开发人员来定义,让另外两端去做适配。在这里以调用NSLog和保存图片为例来详细讲解,测试网页代码取名为test.html,其代码内容如下:
test.html代码内容
test.html代码解释:
可能有些同学对web前端的一些知识不太熟悉,稍微对这段代码做下解释,先说Toyun是iOS和Android这两边在本地要注入的一个对象【参考下面iOS的代码更容易明白】,充当原生应用和web页面之间的一个桥梁。页面上定义了两个按钮名字分别为
IOS代码
通过JSContext,在ObjC中通过JSContext注入模型,然后调用模型的方法:
首先,我们需要先定义一个协议,而且这个协议必须要遵守JSExport协议
接下来,我们还需要定义一个模型,这个模型遵循我们定义的协议
实现协议方法:
接下来,我们在controller中在webview加载完成的代理中,给JS注入模型
####4.2 拦截协议
拦截协议这个适合一些比较简单的一些情况,不需要引入什么框架,只需要web前端配合一下就好。但是在具体调用哪一个方法上,以及在传值的时候可能会有些不方便,而且调用完后无法在回调JavaScript的方法。
test.html中的代码
test.html中的代码解释:
这段代码相比上面的那段测试代码是很简单的,同样有一个按钮,名字为CallCamera点击之后调用自己的callCamera方法,window.location.href这里是改变主窗口的指向从而马上发出一个链接为toyun://callCamera请求,而想要传给原生应用的参数也可已包含到此请求中,而在iOS方法中我们要拦截这个请求,根据请求内容去判断JavaScript想要做的事情,从而实现web页面和本地应用之间的交互。
iOS对应的代码
iOS对应的代码的解释:
在webView的代理方法中去拦截自定义的协议Toyun://如果是此协议则据此判断JavaScript想要做的事情,调用原生应用的方法,这些都是提前约定好的,同时阻止此链接的跳转。
Demo地址:
https://github.com/fuxinto/HfxDemo
###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
相关文章推荐
- IOS-IM即时通讯
- 微信开发者工具
- 正则表达式
- IOS,第三方登陆和分享
- 谈谈Objective-C的警告
- 抓住P2P网贷APP开发的灵魂
- 移动医疗APP开发的核心
- IOS 设置单元格的分界线长度到两边屏幕
- iOS 内购
- Android学习--09-广播
- Spring 初始入口之ApplicationContext 分析
- iOS学习笔记
- sublime用h5开发手机app软件
- 使用 Source InSight 阅读 Android 源码
- iOS开发之Runtime关联属性
- 微信小程序「官方示例代码」剖析【下】:运行机制
- 微信小程序来了 要杀死一切App?
- WebApp/html5高级行业班课程大纲
- cocos2d-x之retain和release
- (OK) 编译 Android, 打印 内核信息 - pr_info - pr_notice