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上,共勉。

相關文章
相關標籤/搜索