最近在iOS项目中需要使用到oc与js之间的相互调用,而且要求是实现方式必须与Android中的相同,方便js中统一处理。于是在对第三方库WebViewJavascriptBridge进行研究之后,仿照Android中的WebView与JS的交互机制,实现了一个,在这里分享给大家。
首先要说明的是,在iOS中js调用Objective-C的代码只能通过重定向的形式进行,即js中通过修改iframe的src,或者直接跳转到一个url,在Objective-C中通过UIWebView的
webView:shouldStartLoadWithRequest:navigationType:方法拦截这个跳转,然后通过解析跳转的url获取js需要调用的方法名和参数。而在Android中,只需要调用WebView的addJavascriptInterface方法,将一个js对象绑定到一个java类,在类中实现相应的函数,当js需要调用java的方法时,只需要直接在js中通过绑定的对象调用相应的函数即可。
显然Android中js交互的方式要比iOS上方便得多,因此,我们可以在iOS上实现一套与Android相类似的机制。下面先说明一下实现的原理,要在js中直接通过绑定的对象调用相应的函数,那么就需要在js中添加相应的代码,但是为了确保与Android的一致性,js代码应该在客户端以注入的形式加入。所以,我们先实现一下需要注入的代码:
详见:DEMO
DEMO2
javaScript:
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if (webView != _webView) { return; }
//is js insert
//这句代码的原理就是如果html文件中不包含在app中定义的方法就在这里获取然后然会给js
if (![[webView stringByEvaluatingJavaScriptFromString: [NSString stringWithFormat:@"typeof window.%@ == 'object'", kBridgeN ame]] isEqualToString:@"true"]) {
//get class method dynamically
unsigned int methodCount = 0;
Method *methods = class_copyMethodList([self class], &methodCount);
NSMutableString *methodList = [NSMutableString string];
for (int i=0; i<methodCount; i++) {
NSString *methodName = [NSString stringWithCString:sel_getName(method_getName(methods[i])) encoding:NSUTF8StringEncoding];
[methodList appendString:@"\""];
[methodList appendString: [methodName stringByReplacingOccurrencesOfString:@":" withString:@"" ]];
[methodList appendString:@"\","];
}
if (methodList.length>0) {
[methodList deleteCharactersInRange:NSMakeRange(methodList.length-1, 1)];
}
NSBundle *bundle = _resourceBundle ? _resourceBundle : [NSBundle mainBundle];
NSString *filePath = [bundle pathForResource:@"WebViewJsBridge" ofType:@"js"];
NSString *js = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
[webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:js, methodList]];
}
__strong typeof(_webViewDelegate) strongDelegate = _webViewDelegate;
if (strongDelegate && [strongDelegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
[strongDelegate webViewDidFinishLoad:webView];
}
}