iOS UIWebView的JavaScriptCore

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

//提供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,直接看代码。css

<!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>
复制代码

实现的效果以下 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能够有两种方式java

  1. 函数调用 直接在js中调用函数并传递必要的参数便可,js代码以下
//js
loadUrl(url, action, token);
复制代码

对应的OC代码以下git

//注意:这里面是子线程
    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提供了相应的方法,github

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

OC的数据类型也能够传唤成JSValueweb

+ (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;
···
复制代码
  1. 调用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方法的。bash

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()"];
复制代码
  1. JSValue调用callWithArguments:(NSArray *)arguments;方法,数组里面是按顺序传的参数
NSString *str1 = @"OC调用JS链接两个字符串";
    NSString *str2 = @"试试好很差用";
[self.jsContext[@"ocToJS"] callWithArguments:@[str1, str2]];
复制代码

效果以下

OC调用JS效果

综上就是JavaScriptCore的基本使用,由于iOS12之后苹果就大力推WKWebView了,因此重心仍是要放在WK上,共勉。

相关文章
相关标签/搜索