鄙人不才献上一份UIWebView和JS交互的桥接框架 不多说直接上货吧!!!
献上框架.m文件的内部实现和代码注释
导入<objc/runtime.h>
#import "UIWebView+Bridge.h"
#import <objc/runtime.h>
#define kBridgeScheme @"bridge"
#define kUIWebViewBridgeRegisteredMethods @"kUIWebViewBridgeRegisteredMethods"
@interface NSString (Params)
- (NSDictionary *)params;
@end
@implementation NSString (Params)
- (NSDictionary *)params {
NSArray *params = [self componentsSeparatedByString:@"&"];
NSMutableDictionary *paramsDict = [NSMutableDictionary dictionary];
for (NSString *query in params) {
NSArray *paramComp = [query componentsSeparatedByString:@"="];
if(paramComp.count > 1) {
NSString *val = [paramComp[1] stringByRemovingPercentEncoding];
NSData* data = [val dataUsingEncoding:NSUTF8StringEncoding];
if(data) {
NSDictionary *valDict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
[paramsDict setObject:valDict forKey:paramComp[0]];
}
}
}
return paramsDict;
}
@end
@interface UIWebView ()
@property (nonatomic,strong) NSMutableDictionary *registeredMethods;
@end
@implementation UIWebView (Bridge)
#pragma mark - 公共方法 -
/**
* 是否是一个JS函数
*
* @param jsFunctionName js函数名
*
* @return 是函数返回YES
*/
- (BOOL)isJSFunction:(NSString *)jsFunctionName {
NSString *result = [self stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"%@&&typeof(%@)==\"function\"",jsFunctionName,jsFunctionName]];
if([result isEqualToString:@"true"]) {
return YES;
}
return NO;
}
/**
* 是否存在js变量
*
* @param jsVariableName 变量名
*
* @return 是否存在
*/
- (BOOL)isJSVariable:(NSString *)jsVariableName {
NSString *result = [self stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"typeof(%@) == \"undefined\"",jsVariableName]];
if([result isEqualToString:@"false"]) {
return YES;
}
return NO;
}
/**
* 向UIWebview注册名称空间
*
* @param nameSpace 名称空间
*/
- (void)createJSNameSpace:(NSString *)nameSpace {
/*
// 对应的js代码
var Namespace = new Object();
Namespace.register = function(path){
var arr = path.split(".");
var ns = "";
for (var i = 0; i < arr.length; i++) {
if (i > 0) {
ns += ".";
}
ns += arr[i];
eval("if(typeof(" + ns + ") == 'undefined') " + ns + " = new Object();");
}
}
*/
if(![self isJSVariable:@"Namespace"]) {
[self stringByEvaluatingJavaScriptFromString:@"var Namespace = new Object();"];
}
if(![self isJSFunction:@"Namespace.register"]) {
[self stringByEvaluatingJavaScriptFromString:
@"var Namespace = new Object();Namespace.register = function(path){var arr = path.split(\".\");var ns = \"\";for (var i = 0; i < arr.length; i++) {if (i > 0) {ns += \".\";}ns += arr[i];eval(\"if(typeof(\" + ns + \") == 'undefined') \" + ns + \" = new Object();\");}}"];
}
if(nameSpace != nil) {
[self stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"Namespace.register(\"%@\")",nameSpace]];
}
}
#pragma mark - 注册方法缓存 -
- (NSMutableDictionary *)registeredMethods {
NSMutableDictionary *registeredMethods = objc_getAssociatedObject(self, kUIWebViewBridgeRegisteredMethods);
if(registeredMethods == nil) {
registeredMethods = [NSMutableDictionary dictionary];
[self setRegisteredMethods:registeredMethods];
}
return registeredMethods;
}
- (void)setRegisteredMethods:(NSMutableDictionary *)registeredMethods {
objc_setAssociatedObject(self, kUIWebViewBridgeRegisteredMethods, registeredMethods, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark - 清空缓存注册方法 -
- (void)clear {
[[self registeredMethods] removeAllObjects];
}
#pragma mark - 注册方法 -
- (void)registerJSMethod:(NSString *)jsMethod target:(id)target method:(SEL)method {
//缓存方法
NSMethodSignature *signature = [[target class] instanceMethodSignatureForSelector:method];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = target;
invocation.selector = method;
NSMutableDictionary *registeredMethods = self.registeredMethods;
[registeredMethods setObject:invocation forKey:jsMethod];
//设置回调函数缓存
if(![self isJSVariable:@"__CallbackCaches__"]) {
[self stringByEvaluatingJavaScriptFromString:@"var __CallbackCaches__ = new Object();"];
}
//运行时植入js代码,使js拥有js回调能力
if(![self isJSFunction:jsMethod]) {
NSString *js = [NSString stringWithFormat:@"%@ = function(params,callback){__CallbackCaches__[\"%@\"]=callback;window.location.href=\"%@://%@?params=\"+encodeURIComponent(JSON.stringify(params));}",jsMethod,jsMethod,kBridgeScheme,jsMethod];
[self stringByEvaluatingJavaScriptFromString:js];
}
}
#pragma mark - 注册url进行调用OC和js -
- (BOOL)dispatchURL:(NSURL *)url {
if([url.scheme isEqualToString:kBridgeScheme]) {
NSString *jsMethod = url.host;
NSDictionary *invokeParams = [url.query params][@"params"];
NSInvocation *invocation = [[self registeredMethods] objectForKey:jsMethod];
[invocation setArgument:&invokeParams atIndex:2];
__autoreleasing NSDictionary *result = nil;
[invocation invoke];
[invocation getReturnValue:&result];
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:result options:NSJSONWritingPrettyPrinted error:nil];
if(jsonData) {
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSString *js = [NSString stringWithFormat:@"__CallbackCaches__[\"%@\"](JSON.parse(decodeURIComponent(\"%@\")))",jsMethod,[jsonString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]]];
[self stringByEvaluatingJavaScriptFromString:js];
}
[self stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"__CallbackMap__[\"%@\"]=NULL;",jsMethod]];
return YES;
}
return NO;
}
@end
.h文件
#import <UIKit/UIKit.h>
/**
* 标准方式桥接js
*/
@interface UIWebView (Bridge)
#pragma mark - JS工具方法 -
/**
* 是否是一个JS函数
*
* @param jsFunctionName js函数名
*
* @return 是函数返回YES
*/
- (BOOL)isJSFunction:(NSString *)jsFunctionName;
/**
* 是否存在js变量
*
* @param jsVariableName 变量名
*
* @return 是否存在
*/
- (BOOL)isJSVariable:(NSString *)jsVariableName;
/**
* 向UIWebview注册名称空间
*
* @param nameSpace 名称空间
*/
- (void)createJSNameSpace:(NSString *)nameSpace;
#pragma mark - 操作方法 -
/**
* 清空已注册的方法
*/
- (void)clear;
/**
* js方法和oc方法之间桥接注册
*
* @param jsMethod js方法名称
* @param target oc消息发送者
* @param method oc消息
*/
- (void)registerJSMethod:(NSString *)jsMethod target:(id)target method:(SEL)method;
/**
* url转发,用于js回调
*
* @param url url跳转回调
*
* @return 返回YES表示跳转成功,并且桥接调用OC代码,返回NO表示失败
*/
- (BOOL)dispatchURL:(NSURL *)url;
@end
那么我们如何使用呢!我给大家演示一下使用步骤吧
- 第一步拦截跳转
#pragma mark - UIWebViewDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
// 1. 框架使用第一步 拦截跳转
return [self.webview dispatchURL:request.URL] == NO;
}
- 第二步 绑定js和oc之间的函数,一定要注意方法名必须和h5那边的方法名一样否则无效
- (void)webViewDidFinishLoad:(UIWebView *)webView {
// 2. 注册 定义JS和OC之间的通讯函数 => 绑定 JS 函数 和 OC 函数
[self.webview registerJSMethod:@"mapLink" target:self method:@selector(mapLink:)];
}
- 第三步 绑定的方法的实现 注意事项:返回值如若没有不能反悔nil,要写成返回空字典的形式
#pragma mark --- Action
// params 参数表示JS调用的时候传递给OC对象的
// 返回值是表示 OC对象把数据返回给JS调用
- (NSDictionary *)mapLink:(NSDictionary *)params {
DEF_DEBUG(@"%@",params);
self.parameters = params[@"mapObj"];
if(_locationBlock)
{
_locationBlock(params[@"mapObj"]);
}
[self.navigationController popViewControllerAnimated:YES];
return @{};
}