UIWebView的应用和其中的JS与OC间传值
现在有很多的应用已经采用了WebView和html语言结合的开发模式。html5一直很火因为一份代码可以在多个平台上运用啊,效果各不相同都很美观,也越来越有一些公司直接招后台程序员和html5程序员,做完的产品再安卓也能用iOS也能用,不用再招双份的工程师了。应用程序一进去就全是UIWebView,里面发个请求到自己用html5做的页面,这就是一个应用!当然今天的主要不是说html5,是说html语言中JS代码和OC代码之间的传值。
先举例一个简单的用法:
我在模仿网易彩票做到设置页面的 常见问题板块时,扒到的素材是一个html网页格式的
也就是说只要设置好了这些按钮点进去都是一个页面,只是每一个按钮都绑定了一个 html.id的属性 ,会html语言的都知道 有了id属性可以直接跳到页面的该位置。
如图点击进入后 会跳到这个界面,点击了如何充值按钮,页面会自动跳到如何充值顶着边。如果点击了如何购彩,页面也会自动跳到自动购彩顶着边。当然了如果倒数第二个倒数第一个估计就跳不上去了,因为下面已经见底了拖上去会弹回来的所以跳不上去了,这点和C#开发的时候是一样的。
此功能的做法:
1
2
3
4
5
6
7
8
|
// 建立webView UIWebView *web = (UIWebView *) self .view; // 发送请求前需要请求和请求的链接 NSURL *url = [[ NSBundle mainBundle] URLForResource:_html.html withExtension: nil ]; NSURLRequest *request = [ NSURLRequest requestWithURL:url]; // $$$$$ [web loadRequest:request]; web.delegate = self ; |
首先要有webView啊,我是手码创建的
上面html是我的模型,html模型里html是一个链接打开本地的一个web文件。你也可以自己找好链接做一个plist再做一个模型
发送请求,接收数据之后,用webView加载数据
然后再给自己的webView设置代理为自己,然后遵守协议UIWebViewDelegate,就可以实现代理方法了
- (void)webViewDidFinishLoad:(UIWebView *)webView
这个方法在页面加载完成后会调用,在这个方法中来做些js的操作。
想做js的操作,一般都是先把你想要的js代码拼接接出一个字符串,再用这个方法传入这个字符串完成操作
1
2
3
4
5
|
// 拼接一个脚本语言中的 自动定位代码 NSString *javascript = [ NSString stringWithFormat:@ "window.location.href = '#%@'" ,_html.ID]; // 加载完成后执行这个代码 [webView stringByEvaluatingJavaScriptFromString:javascript]; |
里面的html.ID是字符串中的一个id属性,控制往哪跳的。
这样核心功能就完成了。
下面再说一个高级点的用法
这是我在模仿网易新闻时做到的新闻详情界面,
如图点击其中的这个库里上篮这一行,进入的新闻详情页面是用html做的,扒到的数据就是这样了,但是数据只有乱七八糟的一大串字符,需要自己排版的,特别是图文混排有点麻烦。。这里今天就不说了,今天主要说下JS和OC间如何传值,关于图文混排 过几天有时间了再详细写一篇。(欢迎关注我评论我)
进去之后是一个新闻详情页面,里面是有图片的,有的图片很好看我就想保存,但是问题来了,webView里面的图片都是<div><img height= src=></img>什么什么的不是UI控件按钮可以绑定事件,imgView至少也可以监听手势点击,这个是网页啊,如何做到点击图片下面弹出一个shit问你保不保存?
这就用到JS代码和OC代码见的传值了,
一般大概思路是这边发请求,那边把请求拦下来,再扒出请求url里的字符串,再各种截取得到有用的数据
核心思路如下,从一半开始截的
1
2
3
4
5
6
7
8
9
10
|
NSString *onload = @ "this.onclick = function() {" " window.location.href = 'sx:src=' +this.src;" "};" ; [imgHtml appendFormat:@ "<img onload=\"%@\" width=\"%f\" height=\"%f\" src=\"%@\">" ,onload,width,height,detailImgModel.src]; // 结束标记 [imgHtml appendString:@ "</div>" ]; // 替换标记 [body replaceOccurrencesOfString:detailImgModel.ref withString:imgHtml options: NSCaseInsensitiveSearch range: NSMakeRange (0, body.length)]; } return body; |
其中onload就是加载完毕后才用,然后onclick是JS里的点击触发,window.location.href 是跳往哪里,后面是一个url 里面的协议头是sx:src= 后面是自己图片的src。 SX是我自己的符号。
就相当于我自己乱写了一个协议头sx://www.啥 ,有了协议头这就是一个跳转网页的请求 但是要注意的是,我把// 和www啥的省略了 但是前面这个sx:src=不能再省略了,如果再省略就不是协议头了,苹果就会自己给你加一个他的协议头很长很乱 后面接不住了。
然后写这个方法 ,webView的代理方法
这个方法是在即将发送请求时会被调用,返回值是一个BOOL类型,说白了就是让你控制什么请求允许发出去,什么请求拦下。
1
2
3
4
5
6
7
8
9
|
NSString *url = request.URL.absoluteString; NSRange range = [url rangeOfString:@ "sx:src=" ]; if (range.location != NSNotFound ) { NSInteger begin = range.location + range.length; NSString *src = [url substringFromIndex:begin]; [ self savePictureToAlbum:src]; return NO ; } return YES ; |
先取出这个请求的全url再算出sx:src=这个字符串的范围,如果是别的真正的请求,是取不到值的,就是NSNotFound,这是允许发出去的
如果取到range了 那就是我写的这个请求,就从这个范围往后的字符串全截取下来,这就是html代码里那个图片的地址了this.src。
然后有了url链接就可以保存到相册了。
保存到相册那个方法里,有两种选择一种是重新下载保存,一种是从缓存中取,这种比较省流量。
1
2
3
4
5
|
NSURLCache *cache =[ NSURLCache sharedURLCache]; NSURLRequest *request = [ NSURLRequest requestWithURL:[ NSURL URLWithString:src]]; NSData *imgData = [cache cachedResponseForRequest:request].data; UIImage *image = [UIImage imageWithData:imgData]; UIImageWriteToSavedPhotosAlbum(image, nil , nil , nil ); |
应该都能看懂的吧,大意就是通过这个url找到我上次请求中用这个url弄到的响应数据再转化成图片保存入相册。
到此就完成了JS与OC间的传值
当然如果只有这样那还远远不够
这里是只有一个保存图片的方法,如果以后又有很多需要拦截的方法,打电话或者发短信等各种各样的方法,都要一一判断就太麻烦了。
聪明的做法是把 需要调用的方法名 和 要传的参数 都通通写在url里让我拦
比如sx:call:&10086
然后我通过字符串的切割就可以 得到一个方法名 call:和一个参数10086
就可以调用- (void)call:(NSString *)phoneNumber 这个方法了。
1
2
3
4
5
6
7
8
9
10
11
|
NSRange range = [url rangeOfString:@ "sx:" ]; if (range.location != NSNotFound ) { NSUInteger loc = range.location + range.length; NSString *path = [url substringFromIndex:loc]; // 获得方法和参数 NSArray *methodNameAndParam = [path componentsSeparatedByString:@ "&" ]; // 方法名 NSString *methodName = [methodNameAndParam firstObject]; // 调用方法 SEL selector = NSSelectorFromString (methodName); if ([ self respondsToSelector:selector]) { // 判断方法的目的: 防止因为方法不存在而报错 |
这一串代码写的很清楚了,获得方法名和参数,把字符串转化成SEL 然后下面就可以这么写了
调用打电话方法 call 传的值是 10086
如果遇到了有的方法需要传多个值。那就该这么写
sx:sendMsg:body:&18686652446&loveyou
通过切割可以得到方法名是 sendMsg:body: 要传的参数用&切开 就是这个(componentsSeparatedByString:@"&")得到两个参数电话号码和信息内容
然后就可以调用这个方法了
这里我要说一下
如果要传入3个4个好多个参数,要用到一个第三方框架
1
2
3
4
5
6
7
8
9
|
// Copyright (c) 2015年 shangxianDante. All rights reserved. // #import <Foundation/Foundation.h> @interface NSObject (Extension) - ( id )performSelector:( SEL )selector withObjects:( NSArray *)objects; @end |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
// Copyright (c) 2015年 shangxianDante. All rights reserved. // #import "NSObject+Extension.h" @implementation NSObject (Extension) - ( id )performSelector:( SEL )selector withObjects:( NSArray *)objects { NSMethodSignature *signature = [ self methodSignatureForSelector:selector]; if (signature) { NSInvocation * invocation = [ NSInvocation invocationWithMethodSignature:signature]; [invocation setTarget: self ]; [invocation setSelector:selector]; for ( int i = 0; i < [objects count]; i++){ id object = [objects objectAtIndex:i]; [invocation setArgument:&object atIndex: (i + 2)]; } [invocation invoke]; if (signature.methodReturnLength) { id anObject; [invocation getReturnValue:&anObject]; return anObject; } else { return nil ; } } else { return nil ; } } @end |
然后就可以用这个方法啦
最后我想再说个简单的小技巧
就是因为现在html5很火,就是一份代码在电脑是一个界面在手机又是一个界面,很漂亮。有时候可以直接搬过来用但是最下面的页脚(也许是广告)又不想要,就可以直接截了,把人家网站搬过来再把人家的页脚截了,有意思吧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
- ( void )viewDidLoad { [ super viewDidLoad]; NSURLRequest *request = [ NSURLRequest requestWithURL:[ NSURL URLWithString:@ "http://v3.bootcss.com/" ]]; self .webView.delegate = self ; [ self .webView loadRequest:request]; } #pragma mark - ******************** web代理方法 /** * 网页加载完毕后调用 */ - ( void )webViewDidFinishLoad:(UIWebView *)webView { NSString *js = @ "document.getElementsByTagName('footer')[0].remove();" ; [webView stringByEvaluatingJavaScriptFromString:js]; |
当然首先你肯定要用浏览器的开发者工具找到页脚这部分板块的名字叫footer啊,然后截了。
(董铂然博客所有文章都是原创欢迎评论和关注)