關于UIWebView與JS的交互:
?這里先聲明一下:示例只放上了重點代碼,后面會給demo地址藐吮。
1、原始交互方法:
1谣辞、OC調用JS:向UIWebView發(fā)送- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
消息來執(zhí)行一段JavaScript腳本迫摔;這里需要注意的是:該方法必須在主線程調用,否則不起作用泥从。
- 同時本人在項目時遇到這樣的需求情況:原生controller進入下一級界面controller句占,該controller存在一個 webView,直接給webView傳遞參數躯嫉,然后點擊H5頁面內按鈕纱烘,webview進入次級界面,再次點擊H5次級頁面內按鈕祈餐,進入下一級原生controller擂啥,而后返回有webView的controller界面并回傳值給webView所在controller,當webView所在controller獲取到返回數據后再傳遞給H5次級頁面帆阳。
- 這里發(fā)生的問題就是啤它,我在最后一步時回傳值使用的是block,在block內部使用
dispatch_async(dispatch_get_main_queue(), ^{})
回歸主線程后再調用stringByEvaluatingJavaScriptFromString:
向H5發(fā)送參數。但是此時并不能將參數發(fā)送成功变骡,即stringByEvaluatingJavaScriptFromString:
不起作用。原因暫時不知芭逝,經排查確實是在主線程了塌碌。 - 解決方法:傳值方式改為通知中心的方式,然后當收到通知后將參數發(fā)送旬盯。
- 這里發(fā)生的問題就是啤它,我在最后一步時回傳值使用的是block,在block內部使用
2台妆、JS調用OC:在UIWebView的代理方法- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
中攔截URL然后重定向去執(zhí)行OC相關代碼;
示例:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
// 原始的JS調用OC胖翰,攔截URL接剩,重定向
// 這里我在 index.html中采用了兩種方式進行request返回,1:onclick="window.open('need://transform')"萨咳;2:onclick="window.location.href='need://location'"
if ([request.URL.absoluteString hasPrefix:@"need://transform"]) {// 跳轉
NSLog(@"執(zhí)行了跳轉操作");
return NO;
}
if ([request.URL.absoluteString hasPrefix:@"need://location"]) {// 本界面的一些操作
NSLog(@"執(zhí)行了本界面操作");
return NO;
}
return YES;
}
// 這里是點擊了OC中一個原生的button所執(zhí)行的方法
- (void)rightButAction:(UIButton *)sender
{
// 使用UIWebView自帶方法調用JS方法懊缺,其中picCallback('%@')是JS方法,后面是參數
NSString * jsStr = [NSString stringWithFormat:@"picCallback('%@')", @"stringByEvaluatingJavaScriptFromString方法實現"];
[self.mainWebView stringByEvaluatingJavaScriptFromString:jsStr];
}
2培他、使用JavaScriptCore:
? 關于JavaScriptCore框架可以參考這篇文章鹃两,當使用時需要先導入該框架頭文件#import <JavaScriptCore/JavaScriptCore.h>
。
1舀凛、OC調用JS: 在代理方法- (void)webViewDidFinishLoad:(UIWebView *)webView
中獲取交互上下文對象(JSContext
) 俊扳,然后調用JSContext
的- (JSValue *)evaluateScript:(NSString *)script;
方法執(zhí)行JS代碼;
2猛遍、JS調用OC:這里有兩種方法馋记,一種是針對JS中未指明調用對象的方法,一種是針對JS中指明調用對象的方法懊烤。
未指明調用對象的方法:可以直接通過context獲取到該方法梯醒,賦予其block的回調方式即可;
指明調用對象的方法:需要創(chuàng)建繼承JSExport的協(xié)議奸晴,協(xié)議方法要與JS中方法相同冤馏!通過context將某一類的實例賦予JS當做調用方法的對象,然后在該類中服從協(xié)議方法即可寄啼;
示例:
// JS調用OC
@protocol JSObjcDelegate <JSExport>
//協(xié)議的方法必須和JS里面的方法名稱保持一致才有效!
- (void)callShare;
@end
@interface JRWebViewMutualViewController ()<UIWebViewDelegate, JSObjcDelegate>
@property (nonatomic,strong) JSContext * jsContext;// 獲取交互環(huán)境逮光,主要用于調取JS代碼
@property(strong,nonatomic)UIWebView * mainWebView;
@end
@implementation JRWebViewMutualViewController
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
self.jsContext = [self.mainWebView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
// 未指明調用對象的方法
self.jsContext[@"callCamera"] = ^() {
NSLog(@"調用Camera了??");
};
/**
在JS中 onclick="callCamera()" 指的是點擊button直接觸發(fā)callCamera方法;
onclick="TEXT.callShare() 指點擊button會讓一個叫做TEXT的對象去觸發(fā)callShare方法;
*/
//在使用JSExport協(xié)議類時必須有指定的執(zhí)行對象才能使用否則使用block形式的回調即可
self.jsContext[@"TEXT"] = self;
// 若發(fā)生異常會執(zhí)行此方法
self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) {
NSLog(@"異常信息是%@",exception);
};
}
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
NSLog(@"加載錯誤:%@", error);
}
// 這里是點擊了OC中一個原生的button所執(zhí)行的方法
- (void)rightButAction:(UIButton *)sender
{
// 獲取 將字符串對應的JS方法墩划,轉換成一個JSValue對象
JSValue * jsValue = [self.jsContext evaluateScript:@"picCallback"];
// 下面??這一方法與上面的等效
// JSValue * jsValue = self.jsContext[@"picCallback"];
// 作為一個函數調用JSValue 參數是JS函數所需參數,該方法用于傳參
[jsValue callWithArguments:@[@"javaScript實現"]];
// 與上面兩句代碼等效代碼
// [self.jsContext evaluateScript:[NSString stringWithFormat:@"picCallback('%@')", @"javaScript實現"]];
}
- (void)callShare
{
NSLog(@"調用Share了??");
}
@end
?在這里因為若沒有HTML的代碼可能會不是那么清晰涕刚,附上HTML的代碼,里面比較簡單:
<!DOCTYPE html>
<html>
<head lang="zh-CN">
<meta charset="UTF-8">
<title>OC-JS交互</title>
</head>
<body>
<div style="margin-top: 30px">
<input type="button" value="調用OC原生代碼示例 - 攔截協(xié)議乙帮,跳轉界面" onclick="window.open('need://transform')" style = "width:300px;height:30px;border:0px;background-color:red;margin-left:10px" >
</div>
<div>
<input type="button" value="調用OC原生代碼示例 - 攔截協(xié)議杜漠,本界面做操作" onclick="window.location.href='need://location'" style = "width:300px;height:30px;border-style:none; background-color:#FF9;margin-left:10px; margin-top:10px">
</div>
<div>
<p><1>和后端同事協(xié)定好協(xié)議,如need://transform表示跳轉,need://location表示本界面的其他操作驾茴。 <br>
<2>實現UIWebView代理的shouldStartLoadWithRequest:navigationType:方法盼樟,在方法中對url進行攔截,如果是步驟 <1> 中定義好的協(xié)議則執(zhí)行對應原生代碼锈至,返回NO進行url攔截晨缴,否則返回YES繼續(xù)加載原url。</p>
<div>
<div style="margin-top: 10px">
<input type="button" value="調用OC原生代碼示例 - JSCore,跳轉界面" onclick="callCamera()" style = "width:300px;height:30px;border:0px;background-color:red;margin-left:10px" >
</div>
<div>
<input type="button" value="調用OC原生代碼示例 - JSCore峡捡,本界面做操作" onclick="TEXT.callShare()" style = "width:300px;height:30px;border-style:none; background-color:#FF9;margin-left:10px; margin-top:10px">
</div>
<script>
var picCallback = function(photos) {
alert(photos);
}
</script>
</body>
</html>
demo 地址击碗,demo是一個項目集合,暫時沒什么東西们拙,會后續(xù)往里面加入稍途,交互界面在左側抽屜中??。