- 获取UIWebView的JSContext
通过
JSContext* context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
获取的context不能保证新建的webview的context能及时的获取到,导致context调用当前环境的js函数失效。
下面我们通过Apple的私有Api获取JSContext ,这个方法是监控JSContext的创建来获取的。经过测试,经过处理过的私有API是可以上架苹果市场的。
/**
其实是private api 通过监控这个方法是否调用实时知道jscontext是否创建;这个是依赖web上是否执行script,执行时会检查是否实现了下面的方法
@param unused class 为WebView
@param ctx 当前UIWebView运行的JSContext环境
@param frame WebFrameLoadDelegate WebKit的WebFrame
*/
- (void) webView: (id) unused didCreateJavaScriptContext: (JSContext*) ctx forFrame: (id<RainWebFrame>) frame
{
NSString *frameMehtod = [NSString stringWithUTF8String:webFrameBase64Str];
NSData *frameData = [[NSData alloc]initWithBase64EncodedString:frameMehtod options:NSDataBase64DecodingIgnoreUnknownCharacters];
NSString *frameStr = [[NSString alloc]initWithData:frameData encoding:NSUTF8StringEncoding];
SEL frameSelector = NSSelectorFromString(frameStr);
// only interested in root-level frames
if ([frame respondsToSelector:frameSelector] && [frame parentFrame] != nil ) {
return;
}
void (^didCreateJavaScriptContext)() = ^{
for (UIWebView *webv in webviews) {
NSString *webIdentifier = [NSString stringWithFormat:@"rain_jsWebView_%lud",(NSUInteger)webv.hash];
NSString *jsAddVariable = [NSString stringWithFormat:@"var %@ = '%@'",webIdentifier,webIdentifier];
[webv stringByEvaluatingJavaScriptFromString:jsAddVariable];//为webView添加个唯一标识符变量
JSValue *identifierJSValue = ctx[webIdentifier];//通过这个获取当前的标识符的值[ctx objectForKeyedSubscript:webIdentifier];
if ([identifierJSValue.toString isEqualToString:webIdentifier]) {
[webv rainDidCreateJavaScriptContext:ctx];
return ;
}
}
};
if ([NSThread isMainThread]) {
didCreateJavaScriptContext();
}else {
dispatch_async(dispatch_get_main_queue(), didCreateJavaScriptContext);
}
}
- Native调用JS函数
多种方式:
/*第一个方式,我把获取到的JSContext作为webview的一个属性了,方便随时拿来使用*/
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[self.webView.context evaluateScript:@"jsFunction('native调用了js的函数')"];
}
/*第二个方式,通过jscontext和jsvalue来调用*/
- (void)webViewDidFinishLoad:(UIWebView *)webView {
JSValue *jsFunctionValue = self.webView.context[@"jsFunction"];
[jsFunctionValue callWithArguments:@[@"native调用了js的函数"]];
}
/*第三个方式:通过webview调用*/
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[self.webView stringByEvaluatingJavaScriptFromString:@"jsFunction('native调用了js的函数')"];
}
- JS调用Native方法
a、通过block设置
self.webview.context[@"jsCallNative"] = ^(NSString *message){
NSLog(message);
}
在h5里直接调用jsCallNative('我来调用Native方法了')就好了
b、通过JSExport协议调用
某个类作为实现JSExport协议,然后把这个类对象作为JS的对象,通过调用JS对象的相应函数即可调起该类对象的方法
定义继承JSExport的协议
@protocol RainJSExportProtocol<JSExport>
/**
js 打印日志时调用这个,方便native查看日志
@param log js打印日志调用的函数名
@param void js方法映射到native上的方法
@return void
*/
JSExportAs(log, - (void)debugLog:(NSString *)message);
/**
js 调用本地定义的一个方法
@param callNative js 调用的函数名
@param void js callNative映射到本地的方法名
@return void
*/
JSExportAs(callNative, - (void)callInterface:(NSString *)interface parameters:(NSString *)jsonStr);
//也可以直接定义方法名,对应js调用的时候去掉冒号:参数第一个字母大写;
//比如 -(void)myName:(NSString *)name age:(NSString *)myage height:(NSString *)myheight; js调用native这个方法时这样调用native.myNameAgeHeight('name','15','172')
@end
以下是实现了RainJSExportProtocol协议的类
#pragma mark-- RainJSExportProtocol
- (void)debugLog:(NSString *)message {
#ifdef DEBUG
NSLog(@"%@",message);
#endif
}
- (void)callInterface:(NSString *)interface parameters:(NSString *)jsonStr {
NSLog(@"interface %@ jsonStr %@",interface,jsonStr);
}
比如该类在JS里被定义为native对象:context[@"native"] = self;//把self当做对jscontext对方开放的对象
那js就可以这样调用
native.log('打印日志');
native. callNative('第一个参数','第二个参数');
更新时间:2018-06-28
网友评论