UIWebView(OC)与JS(JavaScript)交互

  1. 获取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);
        
    }
    
}
  1. 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的函数')"];

}

  1. 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('第一个参数','第二个参数');

放出demo地址

更新时间:2018-06-28

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 前言 Web 页面中的 JS 与 iOS Native 如何交互是每个 iOS 猿必须掌握的技能。而说到 Nati...
    幽城88阅读 6,612评论 1 8
  • 1、通过CocoaPods安装项目名称项目信息 AFNetworking网络请求组件 FMDB本地数据库组件 SD...
    阳明AI阅读 16,039评论 3 119
  • (此文转载) 一、 那天,朱朱画了一幅画,速写纸上铅笔勾勒出淡淡的痕迹,长发随意的散在肩膀,戴着一圈花环,眼神清亮...
    哆啦阿娅阅读 2,047评论 0 0
  • 在这个日益激烈的社会里,离不了竞争。正是有了竞争,才会有社会的进步发展。 《放下头脑,信任身体》教会我们享受当下,...
    王朋彦阅读 1,170评论 0 0
  • 爱情,到底是什么。大学三年,没有耍过一次朋友,在这要离校的时候,我朋友突然给我介绍了一个女生,是他们班上的。那天中...
    victorqin阅读 1,742评论 0 0