如果只有一个webview要想js与ios的本地代码进行通信可以使用现成的phonegap框架,现因项目要求开发ipad版本的应用,页面中有多个需要多个webview所以需要自己编写js代码实现js与本地代码通信。
基本原理是:webview访问特殊的url前缀地址,然后在UIWebViewDelegate的shouldStartLoadWithRequest方法中对url进行过滤就可以达到js与本地代码进行通信了。
1.在MainViewController中实现UIWebViewDelegate,实现shouldStartLoadWithRequest方法:
<p class="p1">#define CALLFUNCTION_PREFIX @<span class="s1">"callfunction://"</span></p>
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ [[UIApplication sharedApplication] sendAction:@selector(resignFirstResponder) to:nil from:nil forEvent:nil]; NSString *url = [request.URL absoluteString]; NSLog(@"should url = %@ webView.tag = %d",url,webView.tag); if ([url hasPrefix:@"http://"] || [url hasPrefix:@"https://"]) { return [self logoutWithAlertWebView:webView url:url]; }else if([url hasPrefix:CALLFUNCTION_PREFIX]){ NSLog(@"webView.tag = %d",webView.tag); [[BasicPlugin getInstance]executePluginByUrl:url tag:webView.tag]; return NO; } return YES; }
2.以下代码通过类名和方法名反射执行指定类的指定方法:
-(void)executePluginByUrl:(NSString *)url tag:(NSInteger)tag{ NSRange range = [url rangeOfString:CALLFUNCTION_PREFIX]; NSString *temp = [url substringFromIndex:range.location + range.length]; NSArray *arr = [temp componentsSeparatedByString:@"&"]; NSString *callBackId = @""; NSString *className = @""; NSString *methodName = @""; NSMutableArray *params = [NSMutableArray arrayWithCapacity:0]; if(arr != nil && arr.count > 0){ NSString *tt = [arr objectAtIndex:0]; NSArray *tempArr = [tt componentsSeparatedByString:@"="]; callBackId = [tempArr objectAtIndex:1]; tt = [arr objectAtIndex:1]; tempArr = [tt componentsSeparatedByString:@"="]; className = [tempArr objectAtIndex:1]; tt = [arr objectAtIndex:2]; tempArr = [tt componentsSeparatedByString:@"="]; methodName = [tempArr objectAtIndex:1]; tt = [arr objectAtIndex:3]; tempArr = [tt componentsSeparatedByString:@"="]; NSString *paramStr = [tempArr objectAtIndex:1]; paramStr = [paramStr stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; if(paramStr != nil && paramStr.length > 0){ params = [NSMutableArray arrayWithArray:[paramStr componentsSeparatedByString:@"$"]]; } //反射调用有参方法 NSMutableArray *pp = [NSMutableArray arrayWithCapacity:0]; [pp addObject:[NSString stringWithFormat:@"%d",tag]]; [pp addObject:callBackId]; for(NSString *t in params){ [pp addObject:t]; } Class cls = NSClassFromString(className); id a= [[cls alloc] init]; SEL selector = NSSelectorFromString([NSString stringWithFormat:@"%@%@",methodName,@":"]); if([a respondsToSelector:selector]){ @try { [a performSelector:selector withObject:pp]; } @catch (NSException *exception) { NSLog(@"BasicPlugin: class:%@'s method:%@ is not found.",className,methodName); } } }
3.js插件编写,使用location.href访问指定的url,这里有一个bug就是,不能同时调用两个插件,因为第二个插件的location.href会覆盖第一个location.href,所以需要等待第一个执行完才可以执行第二个。
//定义插件 $(function(){ // var js2native_exception_arr = ["HomeViewPluginshowMask","HomeViewPluginunMask","HomeViewPluginalert"]; function pageName(){ var strUrl=location.href; var arrUrl=strUrl.split("/"); var strPage=arrUrl[arrUrl.length-1]; return strPage; } if(!window.plugins){ window.plugins = {}; } if(!window.js2native){ window.js2native = { exec : function(className,methodName,params){ var arr = params; var arrStr = ''; if(arr && arr.length > 0){ for(var tt in arr){ arrStr += '$'; arrStr += arr[tt]; } arrStr = arrStr.substring(1); } var url="callfunction://callbackId=" + className + methodName +"Event&className="+className+"&method="+methodName + "¶ms=" + encodeURIComponent(arrStr); url += ("¤tPage=" + pageName()); url += ("&tt=" + new Date().getTime()); var temp = className + methodName; for(var tt in js2native_exception_arr){ var et = js2native_exception_arr[tt]; if(et == temp){ url = "http://" + url; $.get(url); return; } } location.href = url; } }; } //------------------------------插件方法------------------------// //1.InfoPlugin function InfoPlugin() { }; /** * 获取上下文路 */ InfoPlugin.prototype.getCtx = function() { js2native.exec("InfoPlugin", "getCtx",[]); }; window.plugins.infoPlugin = new InfoPlugin(); });
4.其中js2native_exception_arr是为了异步访问后台,需要与NSURLCache一同使用。
@interface LocalSubstitutionCache : NSURLCache<NSURLConnectionDataDelegate>{ NSMutableDictionary *cachedResponses; } - (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request{ // // Get the path for the request // NSString *pathString = [[request URL] absoluteString]; NSString *relativePath=[[request URL] relativePath]; NSString *version = [[request URL] query]; NSLog(@"LocalSubstitutionCache===%@",pathString); if(pathString != nil && ![@""isEqualToString:pathString] && [pathString hasPrefix:EXCEPTION_CALLFUNCTION_PREFIX]){ pathString = [pathString substringFromIndex:[EXCEPTION_CALLFUNCTION_PREFIX length]]; pathString = [NSString stringWithFormat:@"%@%@",EXCEPTION_CALLFUNCTION_REPLACE,pathString]; [[BasicPlugin getInstance]executePluginByUrl:pathString tag:1]; NSLog(@"cachedResponseForRequest.executePluginByUrl = %@",pathString); return [super cachedResponseForRequest:request]; } ......
时间: 2024-10-26 08:28:38