OC与JS交互

关于UIWebView与JS的交互:

 这里先声明一下:示例只放上了重点代码,后面会给demo地址。

1、原始交互方法:

1、OC调用JS:向UIWebView发送- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;消息来执行一段JavaScript脚本;这里需要注意的是:该方法必须在主线程调用,否则不起作用。

  • 同时本人在项目时遇到这样的需求情况:原生controller进入下一级界面controller,该controller存在一个 webView,直接给webView传递参数,然后点击H5页面内按钮,webview进入次级界面,再次点击H5次级页面内按钮,进入下一级原生controller,而后返回有webView的controller界面并回传值给webView所在controller,当webView所在controller获取到返回数据后再传递给H5次级页面。
    • 这里发生的问题就是,我在最后一步时回传值使用的是block,在block内部使用dispatch_async(dispatch_get_main_queue(), ^{})回归主线程后再调用stringByEvaluatingJavaScriptFromString:向H5发送参数。但是此时并不能将参数发送成功,即stringByEvaluatingJavaScriptFromString:不起作用。原因暂时不知,经排查确实是在主线程了。
    • 解决方法:传值方式改为通知中心的方式,然后当收到通知后将参数发送。

2、JS调用OC:在UIWebView的代理方法- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;中拦截URL然后重定向去执行OC相关代码;

示例:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    // 原始的JS调用OC,拦截URL,重定向
    // 这里我在 index.html中采用了两种方式进行request返回,1:onclick="window.open('need://transform')";2:onclick="window.location.href='need://location'"
    if ([request.URL.absoluteString hasPrefix:@"need://transform"]) {// 跳转
        NSLog(@"执行了跳转操作");
        return NO;
    }
    if ([request.URL.absoluteString hasPrefix:@"need://location"]) {// 本界面的一些操作
        NSLog(@"执行了本界面操作");
        return NO;
    }
    return YES;
}

// 这里是点击了OC中一个原生的button所执行的方法
- (void)rightButAction:(UIButton *)sender
{
        // 使用UIWebView自带方法调用JS方法,其中picCallback('%@')是JS方法,后面是参数
        NSString * jsStr = [NSString stringWithFormat:@"picCallback('%@')", @"stringByEvaluatingJavaScriptFromString方法实现"];
        [self.mainWebView stringByEvaluatingJavaScriptFromString:jsStr];
}
2、使用JavaScriptCore:

  关于JavaScriptCore框架可以参考这篇文章,当使用时需要先导入该框架头文件#import <JavaScriptCore/JavaScriptCore.h>
1、OC调用JS: 在代理方法- (void)webViewDidFinishLoad:(UIWebView *)webView中获取交互上下文对象(JSContext) ,然后调用JSContext- (JSValue *)evaluateScript:(NSString *)script;方法执行JS代码;

2、JS调用OC:这里有两种方法,一种是针对JS中未指明调用对象的方法,一种是针对JS中指明调用对象的方法。

  • 未指明调用对象的方法:可以直接通过context获取到该方法,赋予其block的回调方式即可;

  • 指明调用对象的方法:需要创建继承JSExport的协议,协议方法要与JS中方法相同!通过context将某一类的实例赋予JS当做调用方法的对象,然后在该类中服从协议方法即可;

示例:

// JS调用OC
@protocol JSObjcDelegate <JSExport>
//协议的方法必须和JS里面的方法名称保持一致才有效!
- (void)callShare;
@end

@interface JRWebViewMutualViewController ()<UIWebViewDelegate, JSObjcDelegate>
@property (nonatomic,strong) JSContext * jsContext;// 获取交互环境,主要用于调取JS代码
@property(strong,nonatomic)UIWebView * mainWebView;
@end

@implementation JRWebViewMutualViewController

- (void)webViewDidFinishLoad:(UIWebView *)webView
{
    self.jsContext = [self.mainWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    // 未指明调用对象的方法
    self.jsContext[@"callCamera"] = ^() {
        NSLog(@"调用Camera了🙄");
    };
    /**
        在JS中 onclick="callCamera()" 指的是点击button直接触发callCamera方法;
        onclick="TEXT.callShare() 指点击button会让一个叫做TEXT的对象去触发callShare方法;
     */
    //在使用JSExport协议类时必须有指定的执行对象才能使用否则使用block形式的回调即可
    self.jsContext[@"TEXT"] = self;
    // 若发生异常会执行此方法
    self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) {
        NSLog(@"异常信息是%@",exception);
    };
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    NSLog(@"加载错误:%@", error);
}

// 这里是点击了OC中一个原生的button所执行的方法
- (void)rightButAction:(UIButton *)sender
{
        // 获取 将字符串对应的JS方法,转换成一个JSValue对象
        JSValue * jsValue = [self.jsContext evaluateScript:@"picCallback"];
        // 下面👇这一方法与上面的等效
//        JSValue * jsValue = self.jsContext[@"picCallback"];
        // 作为一个函数调用JSValue 参数是JS函数所需参数,该方法用于传参
        [jsValue callWithArguments:@[@"javaScript实现"]];
        
        // 与上面两句代码等效代码
//        [self.jsContext evaluateScript:[NSString stringWithFormat:@"picCallback('%@')", @"javaScript实现"]];
}

- (void)callShare
{
    NSLog(@"调用Share了🙄");
}
@end

 在这里因为若没有HTML的代码可能会不是那么清晰,附上HTML的代码,里面比较简单:

<!DOCTYPE html>  
<html>  
<head lang="zh-CN">
    <meta charset="UTF-8">
    <title>OC-JS交互</title>
</head>  
<body>  
    <div style="margin-top: 30px">
        <input type="button" value="调用OC原生代码示例 - 拦截协议,跳转界面" onclick="window.open('need://transform')" style = "width:300px;height:30px;border:0px;background-color:red;margin-left:10px" >
    </div>         
  
    <div>  
        <input type="button" value="调用OC原生代码示例 - 拦截协议,本界面做操作" onclick="window.location.href='need://location'" style = "width:300px;height:30px;border-style:none; background-color:#FF9;margin-left:10px; margin-top:10px">
    </div>
    
    <div>
        <p>&lt;1&gt;和后端同事协定好协议,如need://transform表示跳转,need://location表示本界面的其他操作。 <br>
        &lt;2&gt;实现UIWebView代理的shouldStartLoadWithRequest:navigationType:方法,在方法中对url进行拦截,如果是步骤 &lt;1&gt; 中定义好的协议则执行对应原生代码,返回NO进行url拦截,否则返回YES继续加载原url。</p>
    <div>
        
    <div style="margin-top: 10px">
        <input type="button" value="调用OC原生代码示例 - JSCore,跳转界面" onclick="callCamera()" style = "width:300px;height:30px;border:0px;background-color:red;margin-left:10px" >
            </div>
    
    <div>
        <input type="button" value="调用OC原生代码示例 - JSCore,本界面做操作" onclick="TEXT.callShare()" style = "width:300px;height:30px;border-style:none; background-color:#FF9;margin-left:10px; margin-top:10px">
            </div>
  
<script>  

    var picCallback = function(photos) {  
        alert(photos);  
    }  

</script>

</body>  
</html>  

demo 地址,demo是一个项目集合,暂时没什么东西,会后续往里面加入,交互界面在左侧抽屉中😆。

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

推荐阅读更多精彩内容