級別:★★☆☆☆
標簽:「iOS與JS交互」「UIWebView與JS交互」「JavaScriptCore」
作者: Xs·H
審校: QiShare團隊
先解釋下標題:“iOS與JS交互”。iOS指iOS原生代碼(文章只有OC示例),JS指WEB前端(不單指JavaScript),交互指JS調(diào)用iOS和iOS調(diào)用JS。
作者將iOS與JS交互總結(jié)成了6種方式,并將逐一介紹。目錄如下:
- iOS與JS交互之UIWebView-協(xié)議攔截
- iOS與JS交互之UIWebView-JavaScriptCore框架
- iOS與JS交互之UIWebView-JSExport協(xié)議
- iOS與JS交互之WKWebView-協(xié)議攔截
- iOS與JS交互之WKWebView-WKScriptMessageHandler協(xié)議
- iOS與JS交互之WKWebView-WKUIDelegate協(xié)議
本文介紹如何使用
JavaScriptCore框架在UIWebView上實現(xiàn)iOS與JS交互。
JavaScriptCore是Apple在iOS7開放的框架。它為在UITableView上實現(xiàn)OC與JS的交互提供了更為簡單的方式。
一、JS調(diào)用iOS:
- 實現(xiàn)邏輯:點擊JS的登錄按鈕,JS將登錄成功后的
token數(shù)據(jù)傳遞給iOS,iOS將收到的數(shù)據(jù)展示出來。 -
實現(xiàn)效果:
JS調(diào)用iOS實現(xiàn)效果 - JS代碼:
//! 登錄按鈕
<button onclick = "login()" style = "font-size: 18px;">登錄</button>
//! 登錄
function login() {
var token = "js_tokenString";
loginSucceed(token);
}
//! 登錄成功
function loginSucceed(token) {
var action = "loginSucceed";
jsToOc(action, token);
}
//! JS調(diào)用OC入口
function jsToOc(action, params) {
var url = "jsToOc://" + action + "?" + params;
loadURL(url);
}
//! 加載URL
function loadURL(url) {
window.location.href = url;
}
- iOS代碼:
//! 導入JavaScriptCore框架頭文件
#import <JavaScriptCore/JavaScriptCore.h>
#pragma mark - UIWebViewDelegate
//! UIWebView在每次加載請求完成后會調(diào)用此方法
- (void)webViewDidFinishLoad:(UIWebView *)webView {
//! 獲取JS代碼的執(zhí)行環(huán)境/上下文/作用域
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//! 監(jiān)聽JS代碼里面的jsToOc方法(執(zhí)行效果上可以理解成重寫了JS的jsToOc方法)
context[@"jsToOc"] = ^(NSString *action, NSString *params) {
dispatch_async(dispatch_get_main_queue(), ^{
[UIWebViewJavaScriptCoreController showAlertWithTitle:action message:params cancelHandler:nil];
});
};
}
- 實現(xiàn)原理:
1、JS與iOS約定好jsToOc方法,作為JS調(diào)用iOS的入口;
2、iOS通過[webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"]獲取JS代碼的上下文JSContext;
3、JS調(diào)用jsToOc方法,iOS使用Block形式監(jiān)聽(重寫)此方法context[@"jsToOc"] = ^() {},收到JS的調(diào)用請求和參數(shù);
PS:在使用
Block形式監(jiān)聽(重寫)JS的方法的時候,不要在Block中直接使用外部的JSValue和JSContent,因為JSContext強引用Block,Block強引用外部變量,JSValue又強引用JSContext(JSValue需要JSContext來執(zhí)行JS代碼),會形成循環(huán)引用。因為JS沒有弱引用的概念,所以__weak不會奏效,可以通過將JSValue作為Block內(nèi)部參數(shù)和[JSContext currentContext]的方式分別解決兩類循環(huán)引用的問題。
二、iOS調(diào)用JS:
實現(xiàn)邏輯:點擊iOS的登錄按鈕,iOS將登錄成功后的token數(shù)據(jù)傳遞給JS,JS將收到的數(shù)據(jù)展示出來。
-
實現(xiàn)效果:
iOS調(diào)用JS實現(xiàn)效果 iOS代碼:
//! 登錄按鈕
UIBarButtonItem *loginBtnItem = [[UIBarButtonItem alloc] initWithTitle:@"登錄" style:UIBarButtonItemStylePlain target:self action:@selector(login:)];
self.navigationItem.rightBarButtonItems = @[loginBtnItem];
//! 登錄方法
- (void)login:(id)sender {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
JSContext *context = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//! JSContext -evaluateScript:方式調(diào)用JS方法
[context evaluateScript:[NSString stringWithFormat:@"ocToJs('loginSucceed', 'oc_tokenString')"]];
//! JSValue -callWithArguments:方式調(diào)用JS方法
// [context[@"ocToJs"] callWithArguments:@[@"loginSucceed", @"oc_tokenString"]];
});
}
- JS代碼:
//! iOS調(diào)用JS入口
function ocToJs(action, params) {
document.getElementById("returnValue").innerHTML = action + '?' + params;
}
//! iOS調(diào)用JS數(shù)據(jù)顯示框
<div id = "returnValue" style = "font-size: 18px; border: 1px dotted; height: 50px;"> </div>
- 實現(xiàn)原理:
1、iOS通過UIWebView的-valueForKeyPath:方法獲取JSContext對象(保證在最新的JS環(huán)境中執(zhí)行JS代碼);
2、iOS通過JSContext的-evaluateScript:方法執(zhí)行一段JS代碼ocToJs('loginSucceed', 'oc_tokenString');
3、JS在ocToJs方法中將iOS傳過來的數(shù)據(jù)顯示在div元素中。
PS:除了使用JSContext的
-evaluateScript:方法之外,還可以先通過[context[@"ocToJs"]獲取到JS的ocToJs方法對應的JSValue,然后使用JSValue的-callWithArguments:方法調(diào)用JS的ocToJs方法。
- 可從QiShare的Github獲取工程源碼

