上個項目用到UIWebView和JS的交互,這里總結一下兩者之間交互的方法歧沪。
以下代碼都在這個demo里骤铃。
先來研究下JS調用OC的方法
攔截假的URL
比如html上有一個按鈕,想要點擊按鈕調用OC的方法礁苗。
在JS中點擊按鈕后用JS發(fā)起一個假的URL請求弟灼,然后利用webView的代理方法shouldStartLoadWithRequest
來拿到這個假的URL字符串,這個字符串包含了需要調用的方法名和傳入的參數冒黑。下面是HTML的代碼
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<p></p>
<div>
<button onclick="showToast();">無參數彈框提示</button>
<button onclick="showToastWithParameter();">有參數彈框提示</button>
<button onclick="shareClick();">Click Me!</button>
</div>
<p></p>
<script>
function showToast() {
window.location.href = 'test1://showToast';
}
function showToastWithParameter() {
window.location.href = 'test1://showToastWithParameter?parama=666666';
}
//不使用window.location.href
function loadURL(url) {
var iFrame;
iFrame = document.createElement("iframe");
iFrame.setAttribute("src", url);
iFrame.setAttribute("style", "display:none;");
iFrame.setAttribute("height", "0px");
iFrame.setAttribute("width", "0px");
iFrame.setAttribute("frameborder", "0");
document.body.appendChild(iFrame);
// 發(fā)起請求后這個 iFrame 就沒用了田绑,所以把它從 dom 上移除掉
iFrame.parentNode.removeChild(iFrame);
iFrame = null;
}
function shareClick() {
loadURL("Test2://shareClick?title=分享的標題&content=分享的內容&url=鏈接地址&imagePath=圖片地址");
}
</script>
</body>
</html>
OC代碼:
- (void)viewDidLoad {
[super viewDidLoad];
_webView = [[UIWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
_webView.delegate = self;
[self.view addSubview:_webView];
NSURL *htmlURL = [[NSBundle mainBundle] URLForResource:@"File.html" withExtension:nil];
NSURLRequest *request = [NSURLRequest requestWithURL:htmlURL];
[_webView loadRequest:request];
// Do any additional setup after loading the view.
}
#pragma mark -- UIWebViewDelegate
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
NSString *scheme = request.URL.scheme;
NSString *host = request.URL.host;
NSString *query = request.URL.query;
NSString * url = request.URL.absoluteString;
if ([scheme isEqualToString:@"test1"]) {
NSString *methodName = host;
if (query) {
methodName = [methodName stringByAppendingString:@":"];
}
SEL sel = NSSelectorFromString(methodName);
NSString *parameter = [[query componentsSeparatedByString:@"="] lastObject];
[self performSelector:sel withObject:parameter];
return NO;
}else if ([scheme isEqualToString:@"test2"]){//JS中的是Test2,在攔截到的url scheme全都被轉化為小寫。
NSURL *url = request.URL;
NSArray *params =[url.query componentsSeparatedByString:@"&"];
NSMutableDictionary *tempDic = [NSMutableDictionary dictionary];
for (NSString *paramStr in params) {
NSArray *dicArray = [paramStr componentsSeparatedByString:@"="];
if (dicArray.count > 1) {
NSString *decodeValue = [dicArray[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[tempDic setObject:decodeValue forKey:dicArray[0]];
}
}
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"方式一" message:@"這是OC原生的彈出窗" delegate:self cancelButtonTitle:@"收到" otherButtonTitles:nil];
[alertView show];
NSLog(@"tempDic:%@",tempDic);
return NO;
}
return YES;
}
- (void)webViewDidStartLoad:(UIWebView *)webView{
}
- (void)webViewDidFinishLoad:(UIWebView *)webView{
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{
}
- (void)showToast {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:@"JS調用OC代碼成功抡爹!" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
}
- (void)showToastWithParameter:(NSString *)parama {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:[NSString stringWithFormat:@"JS調用OC代碼成功 - JS參數:%@",parama] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
}
需要注意的是,在UIWebView的代理方法中攔截到的scheme會轉換成小寫
點擊模擬器上的第一個和第二個按鈕分別會調用本地的showToast和showToastWithParameter:方法。第三個按鈕中沒有使用
window.location.href
,在別的文章中看到了下面的解釋:
因為如果當前網頁正使用window.location.href加載網頁的同時超埋,調用window.location.href去調用OC原生方法卦溢,會導致加載網頁的操作被取消掉。
同樣的泵殴,如果連續(xù)使用window.location.href執(zhí)行兩次OC原生調用涮帘,也有可能導致第一次的操作被取消掉。所以我們使用自定義的loadURL笑诅,來避免這個問題调缨。loadURL的實現來自關于UIWebView和PhoneGap的總結一文疮鲫。
使用JavaScriptCore
iOS7之后,app新添加了一個JavaScriptCore/JavaScriptCore.h
這個庫用來做JS交互弦叶。注意俊犯,使用這種方式調用到OC的方法是在子線程中的。使用JavaScriptCore/JavaScriptCore.h
做js調用oc有兩種方式伤哺,一種是用block注入燕侠,一種是使用JSExport協(xié)議來實現。
下面是h5的代碼:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<p></p>
<div>
<button onclick="showToast();">彈框提示</button>
<button onclick="JSObjective.showAlert('參數1','參數2');">alert提示</button>
</div>
<p></p>
<script>
function showToast() {
// 調用OC中的showToastWithparams方法
showToastWithparams("參數1","參數2","參數3");
}
</script>
</body>
</html>
在HTML中立莉,添加一個事件有兩種方法绢彤,一種是<button onclick="showToast();">彈框提示</button>
,另一種是<button onclick="JSObjective.showAlert('參數1','參數2');">alert提示</button>
;對于第一種寫法,對應的OC代碼如下:
- (void)webViewDidFinishLoad:(UIWebView *)webView{
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
context[@"showToastWithparams"] = ^() {
//NSLog(@"當前線程 - %@",[NSThread currentThread]);
NSArray *params = [JSContext currentArguments];
for (JSValue *Param in params) {
NSLog(@"%@", Param); // 打印結果就是JS傳遞過來的參數
}
//注意桃序,這時候是在子線程中的杖虾,要更新UI的話要回到主線程中
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:[NSString stringWithFormat:@"js調用oc原生代碼成功!"] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
});
};
}
對于第二種寫法,首先要定義一個繼承自JSExport的協(xié)議媒熊,協(xié)議里面定義的方法即是供JS調用的OC原生方法
@protocol TestJSExport<JSExport>
JSExportAs
(showAlert,
- (void)showAlertWithParameters:(NSString *)parameterone parametertwo:(NSString *)parametertwo
);
@end
- (void)webViewDidFinishLoad:(UIWebView *)webView{
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
// 用TestJSExport協(xié)議來關聯關聯JCObjective的方法
context[@"JSObjective"] = self;
}
#pragma mark -- TestJSExport協(xié)議
- (void)showAlertWithParameters:(NSString *)parameterone parametertwo:(NSString *)parametertwo {
NSLog(@"當前線程 - %@",[NSThread currentThread]);// 子線程
//NSLog(@"JS和OC交互 - %@ -- %@",parameterone,parametertwo);
//注意奇适,這個代理方法也是在子線程中的
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"成功" message:[NSString stringWithFormat:@"js調用oc原生代碼成功! - JS參數:%@,%@",parameterone,parametertwo] delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
});
}
WebViewJavascriptBridge
WebViewJavascriptBridge
是一個第三方的庫用于和js交互的。它在做UIWebView的js調用oc方法的時候使用的是攔截假的url的方式芦鳍。