美文网首页iOS高级开发
iOS OC与JS交互(二)--UIWebView的JavaSc

iOS OC与JS交互(二)--UIWebView的JavaSc

作者: 淡定的笨鸟 | 来源:发表于2019-05-10 13:27 被阅读0次

JavaScriptCoreSafari的一个JavaScript引擎,后在iOS7.0中被变成了iOS的一个框架,用做OC与JS之间的桥接,使得OC与JS交互更加方便简单。
JavaScriptCoreDemo
我们在JavaScriptCore.h中可以看到如下五个类

//提供OC与JS交互的上下文环境
#import "JSContext.h"  
//JS的数据,一般是变量、对象和函数
#import "JSValue.h"  
 //管理JSValue的内存
#import "JSManagedValue.h" 
//一个虚拟资源空间,里面有着JSContext对象,各个JSContext对象可相互传值
#import "JSVirtualMachine.h" 
 //这是一个协议,可以通过这个协议实现用js调用OC的对象达到JS调用OC的效果
#import "JSExport.h" 
JS

先写JS,直接看代码。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
                <title>UIWebView中OC与JS交互</title>
                </head>
    
    <style type="text/css">
        .button {
            background: #f5be14;
            margin: 10px;
            text-align: center;
            width: 300px;
            height: 44px;
            line-height: 44px;
            margin: 10px auto;
            border-radius: 5px;
        }
    #setImage {
        width: 200px;
        height: 200px;
        margin: 0 auto;
    }
    #setText {
        width: 200px;
        height: 200px;
        margin: 0 auto;
    }
    </style>
    
    <body>
        
        <div class="button" onclick="firstClick()">今晚打老虎</div>
        <div class="button" onclick="secondClick()">请用力的点我</div>
        <div class="button" onclick="thirdClick()">弹我弹我</div>
        <div class="button" onclick="forthClick()">选择图片</div>
        <div class="button" onclick="callOCToCallJSClick()">调用OC执行JS来连接两个字符串</div>
        <div id="setImage"></div>
        <div id="setText"></div>
    </body>
    
    <script type="text/javascript">
        
        var prefixStandard = "LFJSToOC://";
        
        function getText(index) {
            return document.getElementsByClassName("button")[index].innerText;
        }
    
    function firstClick() {
        var action = "firstClick";
        var token = getText(0);
        alert(jsToOC_Object.getConnectText("我是唐伯虎", token));
    }
    
    function secondClick() {
        var action = "secondClick";
        var token = getText(1);
        setUrl(action, token);
    }
    
    function thirdClick() {
        var action = "thirdClick";
        var token = getText(2);
        setUrl(action, token);
    }
    
    function forthClick() {
        var action = "forthClick";
        var token = getText(3);
        jsToOC_Object.getSystemImage();
    }
    
    function callOCToCallJSClick() {
        var action = "callOCToCallJSClick";
        var token = getText(4);
        setUrl(action, token);
    }
    
    function setUrl(action, token) {
        var url = prefixStandard + "/" + action + "/" + token;
        //这个loadUrl就是调用OC的执行函数
        loadUrl(url, action, token);
    }
    
    function showImageOnDiv(imageStr) {
        var imgDiv = document.getElementById("setImage");
        imgDiv.innerHTML = "<image style='width:200px;' src='data:image/png;base64,"+imageStr+"'>";
    }
    
    //OC调用JS的函数
    function ocToJS(str1, str2) {
        var str = str1 + " OCTOJS " + str2;
        var textDiv = document.getElementById("setText");
        textDiv.innerHTML = str;
    }
    
        </script>
    
</html>

实现的效果如下


JS效果图
获取JSContext

我们在webViewDidFinishLoad代理方法中获取JS的上下文JSContext。

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    NSString *title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
    self.title = title;
    NSLog(@"webView加载完成");
    
    //拦截JS的回调
    JSContext *jsContext = self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
}
JS调用OC

在JavaScriptCore中,JS调用OC可以有两种方式
1、函数调用
直接在js中调用函数并传递必要的参数即可,js代码如下

//js
loadUrl(url, action, token);

对应的OC代码如下

    //注意:这里面是子线程
    jsContext[@"loadUrl"] = ^(JSValue *value, NSString *action, NSString *token) {
        NSLog(@"value = %@", value);
        NSLog(@"action = %@", action);
        NSLog(@"token = %@", token);
    
        dispatch_async(dispatch_get_main_queue(), ^{
            SEL sel = NSSelectorFromString([NSString stringWithFormat:@"%@:", action]);
            objc_msgSend(self, sel, token);
        });
    };

注意:因为JSValueJSContext是强引用的关系,所以我们这里要注意不能在block内使用外部的JSValue对象,会造成循环引用。
JSValue就是js的数据类型,我们可以根据自己的需要转换成OC类型,JSValue提供了相应的方法,

- (BOOL)toBool;
- (double)toDouble;
- (int32_t)toInt32;
- (uint32_t)toUInt32;
- (NSNumber *)toNumber;
- (NSString *)toString;
- (NSDate *)toDate;
- (NSArray *)toArray;
- (NSDictionary *)toDictionary;
···

OC的数据类型也可以传唤成JSValue

+ (JSValue *)valueWithObject:(id)value inContext:(JSContext *)context;
+ (JSValue *)valueWithBool:(BOOL)value inContext:(JSContext *)context;
+ (JSValue *)valueWithDouble:(double)value inContext:(JSContext *)context;
+ (JSValue *)valueWithInt32:(int32_t)value inContext:(JSContext *)context;
+ (JSValue *)valueWithUInt32:(uint32_t)value inContext:(JSContext *)context;
···

2、调用OC的对象实现交互
js代码如下

js
jsToOC_Object.getConnectText("我是唐伯虎", token)

这个jsToOC_Object是我们在OC中创建的对象,getConnectText是我们在OC中实现的方法,上代码

@protocol JSToOC_Protocol <JSExport, NSObject>
//打开系统相册获取图片
- (void)getSystemImage;
//连接两个字符串
JSExportAs(getConnectText,
           - (NSString *)connetText1:(NSString *)str1 withText2:(NSString *)str2);

@end

@interface JSToOC_Object : NSObject <JSToOC_Protocol>

@property (nonatomic, weak) id<JSToOC_Protocol> delegate;

@end

@implementation JSToOC_Object

- (void)getSystemImage {
    NSLog(@"JS调用了打开系统相册, 这里走的是子线程,如果对UI操作需要回到主线程");
    dispatch_async(dispatch_get_main_queue(), ^{
        if (self.delegate && [self.delegate respondsToSelector:@selector(getSystemImage)]) {
            [self.delegate getSystemImage];
        }
    });
}

//连接字符串1和字符串2
- (NSString *)connetText1:(NSString *)str1 withText2:(NSString *)str2 {
    NSString *connectText = [NSString stringWithFormat:@"%@,我选择%@", str1, str2];
    return connectText;
}
@end

如代码所示,需要定义一个遵守JSExport的协议,这个协议里面实现的方法就是可以供js直接调用的方法,这里面可以不用JSExportAs也能直接使用。
然后我们需要在webViewDidFinishLoad代理里面告诉JSContext我们有这么一个对象专门供js调用OC方法的。

JSToOC_Object *jsToOC_Object = [JSToOC_Object new];
//把OC的JSToOC_Object对象传递给JS,供JS调用OC方法
jsContext[@"jsToOC_Object"] = jsToOC_Object;

JS调用OC后,OC的响应也相对应的有block和JSExport两种方式,他们有个共同点需要注意,回调都是在子线程里。
做完这些,以后只要和前端定义方法名和参数,就能随时调用了。

OC调用JS

1、UIWebView本身可以调用stringByEvaluatingJavaScriptFromString
2、JSContext调用evaluateScript

[self.jsContext evaluateScript:@"showMessage()"];

3、JSValue调用callWithArguments:(NSArray *)arguments;方法,数组里面是按顺序传的参数

NSString *str1 = @"OC调用JS连接两个字符串";
    NSString *str2 = @"试试好不好用";
[self.jsContext[@"ocToJS"] callWithArguments:@[str1, str2]];

效果如下


OC调用JS效果

综上就是JavaScriptCore的基本使用,因为iOS12以后苹果就大力推WKWebView了,所以重心还是要放在WK上,共勉。

相关文章

网友评论

    本文标题:iOS OC与JS交互(二)--UIWebView的JavaSc

    本文链接:https://www.haomeiwen.com/subject/jkxgoqtx.html