iOS中用来加载网页,有两个控件UIWebView(iOS8之前),WKWebView(iOS8诞生)。WKWebView比UIWebView有太多的优势了,加载速度快,交互便捷等等。iOS8之后肯定要适配WKWebView。如果要适配到iOS7,项目里就必须要用这两个控件了。下面具体说一下这两个控件与JS的交互。
UIWebView交互Demo:http://onarkxpx4.bkt.clouddn.com/WebViewJavascriptBridgeDemo-master.zip
WKWebView交互Demo:http://onarkxpx4.bkt.clouddn.com/WKWebView与JS交互.zip
UIWebView交互
- 代理方法交互 (自带方法)
拦截链接,从而获取链接里的信息,只能进行JS专递信息到OC。非常简陋,只适用超轻量级交互。
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
// NSLog(@"Scheme: %@", [url scheme]); //方案
// NSLog(@"Host: %@", [url host]); //主机
// NSLog(@"Port: %@", [url port]); //端口
// NSLog(@"Path: %@", [url path]); //路径
// NSLog(@"Relative path: %@", [url relativePath]); //相关路径
// NSLog(@"Path components as array: %@", [url pathComponents]); //路径的每个组成部分
// NSLog(@"Parameter string: %@", [url parameterString]); //参数
// NSLog(@"Query: %@", [url query]); //查询
// NSLog(@"Fragment: %@", [url fragment]); //分段
//当前的链接
NSURL* url = [request URL];
NSString * string = [url absoluteString];
//链接转为字典,获取我们想要的信息
NSDictionary* dic = [self dictionaryFromQuery:string usingEncoding:NSUTF8StringEncoding];
NSLog(@"%@",dic);
//return NO 不允许跳转链接
return YES;
}
链接转字典的工具方法
- (NSDictionary*)dictionaryFromQuery:(NSString*)query usingEncoding:(NSStringEncoding)encoding {
NSCharacterSet* delimiterSet = [NSCharacterSet characterSetWithCharactersInString:@"&;"];
NSMutableDictionary* pairs = [NSMutableDictionary dictionary];
NSScanner* scanner = [[NSScanner alloc] initWithString:query];
while (![scanner isAtEnd]) {
NSString* pairString = nil;
[scanner scanUpToCharactersFromSet:delimiterSet intoString:&pairString];
[scanner scanCharactersFromSet:delimiterSet intoString:NULL];
NSArray* kvPair = [pairString componentsSeparatedByString:@"="];
if (kvPair.count == 2) {
NSString* key = [[kvPair objectAtIndex:0]
stringByReplacingPercentEscapesUsingEncoding:encoding];
NSString* value = [[kvPair objectAtIndex:1]
stringByReplacingPercentEscapesUsingEncoding:encoding];
[pairs setObject:value forKey:key];
}
}
return [NSDictionary dictionaryWithDictionary:pairs];
}
- WebViewJavascriptBridge(第三方)
传送门:https://github.com/marcuswestin/WebViewJavascriptBridge/
UIWebView给的原生方法里对OC与JS交互太乏力了。所以作为github上8千多star的第三方框架,WebViewJavascriptBridge作为UIWebView交互工具无疑是极为优秀的,使用也极为简单。
OC代码
JS调用OC方法(JS给OC传数据)
//基本设置
[WebViewJavascriptBridge enableLogging];//开启日志
WebViewJavascriptBridge* bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];
[bridge setWebViewDelegate:self];
//方法注册,AppClosePrice为方法名
[bridge registerHandler:@"AppClosePrice" handler:^(id data, WVJBResponseCallback responseCallback) {
//data为JS传来的数据
NSLog(@"%@", data);
[self.navigationController popViewControllerAnimated:YES];
}];
** OC调用JS方法(OC给JS传数据,也可以接收JS传过来的数据)**
基本设置同上,就是注册方法的时候不同。
//AppHidden:方法名,appCurVersion:OC给JS传的数据
[self.bridge callHandler:@"AppHidden" data:appCurVersion responseCallback:^(id responseData) {
NSLog(@"----JS传过来的数据----%@",responseData);
}];
JS代码(由后台实现,可以给后台参考)
/*这段代码是固定的,必须要放到js中*/
function setupWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
window.WVJBCallbacks = [callback];
var WVJBIframe = document.createElement('iframe');
WVJBIframe.style.display = 'none';
WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
document.documentElement.appendChild(WVJBIframe);
setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
}
/*与OC交互的所有JS方法都要放在此处注册,才能调用通过JS调用OC或者让OC调用这里的JS*/
setupWebViewJavascriptBridge(function(bridge) {
var uniqueId = 1
function log(message, data) {
var log = document.getElementById('log')
var el = document.createElement('div')
el.className = 'logLine'
el.innerHTML = uniqueId++ + '. ' + message + ':<br/>' + JSON.stringify(data)
if (log.children.length) {
log.insertBefore(el, log.children[0])
} else {
log.appendChild(el)
}
}
/* Initialize your app here */
/*注册一个JS调用OC的方法,不带参数*/
bridge.registerHandler('AppClosePrice', function() {
log("JS调用OC")
})
/*注册一个JS调用OC的方法,带参数*/
bridge.registerHandler('AppClosePrice', function(data, responseCallback) {
log("Get user information from OC: ", data)
})
/*注册一个OC调用JS的方法*/
bridge.callHandler('AppHidden', function(responseData) {
log("JS call ObjC's getUserIdFromObjC function, and js received response:", responseData)
})
WKWebView交互
WKWebView作为UIWebView的替代品,具有完备的JS交互方法,我们可以不借助第三方,就进行OC与JS的深度交互。并且JS端的代码也极其简易。
JS调用OC方法(JS给OC传数据)
交互的方法名定义好,OC,JS都需要进行注册。
OC端注册方法(IOSClosePrice)
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = [[WKUserContentController alloc] init];
[config.userContentController addScriptMessageHandler:self name:@"IOSClosePrice"];
WKWebView *webView = [[WKWebView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT - 14) configuration:config];
JS端注册方法(IOSClosePrice)
function payClick() {
window.webkit.messageHandlers.IOSClosePrice.postMessage({order_no:'201511120981234',channel:'wx',amount:1,subject:'粉色外套'});
}
OC端接收数据
WKWebView的协议WKScriptMessageHandler里面的方法,JS向OC传任何数据,OC都可以通过该方法接收,通过JS传过来的数据,我们可以调用相应OC的方法,从而达到JS调用OC方法的目的。
/**
* JS给原生传数据:JS调用原生的方法
*/
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
NSDictionary *dict = message.body;
NSString* action = [dict valueForKey:@"action"];
NSLog(@"----WKwebView交互----%@",action);
if ([action isEqualToString:@"action1"]) {
[self action1];
}
if ([action isEqualToString:@"action2"]) {
[self action2];
}
}
OC调JS的方法(OC向JS传数据)
OC端代码
payResult 为规定好的方法名,JS端用这个方法名接收数据。
// 将支付结果返回给js
//这里的payResult()是JS的语言
NSString *jsStr = [NSString stringWithFormat:@"payResult('%@')",@"支付成功"];
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@----%@",result, error);
}];
注意:当JS调用OC的时候也可以用这个方法给JS进行回传数据,和上面的WKScriptMessageHandler代理中的方法相结合,就是实现了OC和JS的数据交流
JS端代码
function payResult(str) {
asyncAlert(str);
document.getElementById("returnValue").value = str;
}
总结
从上面分析可以看出,UIWebView作为古老的网页加载控件,只能靠三方才能进行方便的JS交互,虽然WebViewJavascriptBridge使用起来很方便,但是局限性还是有的。而WKWebView作为UIWebView的替代。在OC与JS交互上面体现了强大的能力。WKWebView更注重的加强了OC和JS之间的数据通讯。双方的代码量都极为简洁,数据交互也一目了然。
网友评论
//这里的payResult()是JS的语言
NSDictionary* dic = [[NSDictionary alloc]init];
NSString *jsStr = [NSString stringWithFormat:@"payResult('%@')",dic];
[self.webView evaluateJavaScript:jsStr completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@----%@",result, error);
}];
NSLog(@"----JS传过来的数据----%@",responseData);
}];
这个方法中appCurVersion可以是字符串,也可以是字典。