WKWebView OC與JS交互 同步返回值
<font size=5 color='#333333'>以下主要講WKWebView中OC與JS交互,UIWebView這里稍微簡(jiǎn)單介紹
</font>
<font size=3>這里重點(diǎn)關(guān)于wkwebview中JS調(diào)用OC<font color='#20B2AA'>返回值的問(wèn)題</font>袱讹,普通的OC與JS交互網(wǎng)上資料一大堆</font>
一辽幌、OC與JS交互
<font size=4 color='#666666'>稍微簡(jiǎn)單介紹下:</font>
1.1.JavaScript —> Objective-C
1.1.1.第一種JS調(diào)用OC window.webkit.messageHandlers.<方法名>.postMessage(<數(shù)據(jù)>)
- 向WKWebViewConfiguration實(shí)例中注入OC對(duì)象
// 這里只注冊(cè)一個(gè)方法足矣,通過(guò)參數(shù)functionName區(qū)分調(diào)用OC相應(yīng)方法
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
...
[config.userContentController addScriptMessageHandler:self name:@"Call" ];
- JS方法
// 這里使用window.webkit.messageHandlers.<方法名>.postMessage(<數(shù)據(jù)>) 沒(méi)有直接返回值功能
var JS_Fun_01 = function (){
var args = arguments;
var args_num = arguments.length;
window.webkit.messageHandlers.Call.postMessage({
'functionName':'OC_Fun_01',
'arguments': args_num == 0 ? {} : JSON.stringify(args),
});
};
var JS_Fun_02 = function (){
var args = arguments;
var args_num = arguments.length;
window.webkit.messageHandlers.Call.postMessage({
'functionName':'OC_Fun_02',
'arguments': args_num == 0 ? {} : JSON.stringify(args),
});
};
var JS_Fun_03 = function (){
var args = arguments;
return JSON.stringify(args);
};
<font size=3 color='#666666'>下面是<font color=red>錯(cuò)誤</font>調(diào)用:</font>
// 假如有個(gè)需求通過(guò)調(diào)用OC方法給個(gè)返回值
// 下面這個(gè)方法是錯(cuò)誤的 window.webkit.messageHandlers.Call.postMessage沒(méi)有返回值功能
var JS_Fun_Error = function (){
var result = window.webkit.messageHandlers.Call.postMessage({
'functionName':'OC_Fun_01',
'arguments':{},
});
return result;
};
- OC方法
// JS -> OC
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
if ([message.name isEqualToString:@"Call"])
{
NSString *functionName = [message.body valueForKey:@"functionName"];
NSDictionary *arguments = [message.body valueForKey:@"arguments"];
if ([functionName isEqualToString:@"OC_Fun_01"])
{
[self OC_Fun_01:arguments];
}
else if ([functionName isEqualToString:@"OC_Fun_02"])
{
[self OC_Fun_02:arguments];
}
}
}
- (void)OC_Fun_01:(id)arguments
{
}
- (void)OC_Fun_02:(id)arguments
{
}
...
1.1.2.第二種JS調(diào)用OC URL請(qǐng)求截獲
<font size=3 color='#999999'>JavaScript 在瀏覽器環(huán)境中發(fā)出URL請(qǐng)求, Objective-C 截獲請(qǐng)求以獲取相關(guān)請(qǐng)求的思路. 在Objective-C 中在實(shí)現(xiàn)UIWebViewDelegate 時(shí)截獲請(qǐng)求:</font>
// UIWebView
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSURL *URL = request.URL;
// if (url是自定義的JavaScript通信協(xié)議) {
//
// do something
//
// 返回 NO 以阻止 `URL` 的加載或者跳轉(zhuǎn)
// return NO;
// }
}
// WKWebView
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
NSURL *URL = navigationAction.request.URL;
// if (url是自定義的JavaScript通信協(xié)議) {
//
// do something
//
// decisionHandler(WKNavigationActionPolicyCancel); 以阻止 `URL` 的加載或者跳轉(zhuǎn)
// decisionHandler(WKNavigationActionPolicyCancel);
// return;
// }
decisionHandler(WKNavigationActionPolicyAllow);
}
JavaScript有各種不同的方式發(fā)出URL 請(qǐng)求:
第一種:location.href : 修改window.location.href 替換成一個(gè)合成的URL, 比如 async://method:args
第二種:location.hash : 修改 window.location.hash
第三種:iframe
1.1.3.第三種JS調(diào)用OC 監(jiān)聽(tīng)Cookie
// Objective-C 可以通過(guò)NSHTTPCookieManagerCookiesChangedNotification 事件以監(jiān)聽(tīng)cookie的變化.
// 當(dāng)JavaScript 修改 document.cookie 后, Objective-C 可以通過(guò)分析cookie以得到信息.
NSNotificationCenter *center = NSNotificationCenter.defaultCenter;
[defaultCenter addObserverForName:NSHTTPCookieManagerCookiesChangedNotification
object:nil
queue:nil
usingBlock:^(NSNotification *notification) {
NSHTTPCookieStorage *cookieStorage = notification.object;
// do something with cookieStorage
}];
1.1.4.第四種JS調(diào)用OC <JavaScriptCore/JavaScriptCore>,<font size=4 color=red>可同步返回值</font>
// 此處無(wú)返回值寫(xiě)法
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
context[@"JS_Fun_04"] = ^() {
//用數(shù)組接收傳過(guò)來(lái)的多個(gè)參數(shù)
NSArray *paramArray = [JSContext currentArguments];
};
// 此處有回值寫(xiě)法
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
__weak JSContext *theContext = context;
context[@"JS_Fun_04"] = ^JSValue *() {
//用數(shù)組接收傳過(guò)來(lái)的多個(gè)參數(shù)
NSArray *paramArray = [JSContext currentArguments];
return [JSValue valueWithObject:@"這里是返回值" inContext:theContext];
};
<font size=3>以上前三種方式<font size=5 color=red>缺點(diǎn)</font></font>
window.webkit.messageHandlers.<方法名>.postMessage(<數(shù)據(jù)>)鲁猩、URL請(qǐng)求截獲、監(jiān)聽(tīng)Cookie的三種方式,整個(gè)過(guò)程是異步砰蠢,不能同步
在JavaScript中不能直接獲取Objective-C處理的返回值,需要Objective-C 調(diào)用JavaScript層自己實(shí)現(xiàn)的api才能得到返回值
使用callback 比較麻煩,需要在JavaScript 上自己實(shí)現(xiàn)
1.2.Objective-C —> JavaScript
// WKWebView
[wkWebView evaluateJavaScript:@"JS_Fun_03('參數(shù)一','參數(shù)二')" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
NSLog(@"%@", result);
}];
// UIWebView的第一種
NSString *result = [webView stringByEvaluatingJavaScriptFromString:@"JS_Fun_03('參數(shù)一','參數(shù)二')"];
// UIWebView的第二種
JSContext *context = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
id result = [context evaluateScript:@"JS_Fun_03('參數(shù)一','參數(shù)二')"];
JavaScriptCore 各種類型數(shù)據(jù):
Objective-C type | JavaScript type
--------------------+---------------------
nil | undefined
NSNull | null
NSString | string
NSNumber | number, boolean
NSDictionary | Object object
NSArray | Array object
NSDate | Date object
NSBlock (1) | Function object (1)
id (2) | Wrapper object (2)
Class (3) | Constructor object (3)
二磅网、WKWebView實(shí)現(xiàn)JS調(diào)用OC 同步返回值 <font color=red>重點(diǎn)?? 重點(diǎn)???? 重點(diǎn)?????? </font>
js:
var JS_Fun_05 = function (){
var args = arguments;
var type = "JSbridge";
var functionName = "OC_Fun_05";
var payload = {"type": type, "functionName": functionName, "arguments": args};
var res = prompt(JSON.stringify(payload));
};
oc:
self.webView.UIDelegate = self;
// JS端調(diào)用prompt函數(shù)時(shí),會(huì)觸發(fā)此代理方法竹捉。
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {
NSError *err = nil;
NSData *dataFromString = [prompt dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *payload = [NSJSONSerialization JSONObjectWithData:dataFromString options:NSJSONReadingMutableContainers error:&err];
if (!err)
{
NSString *type = [payload objectForKey:@"type"];
if (type && [type isEqualToString:@"JSbridge"])
{
NSString *returnValue = @"";
NSString *functionName = [payload objectForKey:@"functionName"];
NSDictionary *args = [payload objectForKey:@"arguments"];
if ([functionName isEqualToString:@"OC_Fun_05"])
{
returnValue = [self OC_Fun_05:args];
}
else if ([functionName isEqualToString:@"OC_Fun_06"])
{
returnValue = [self OC_Fun_06:args];
}
completionHandler(returnValue);
}
}
}
- (NSString *)OC_Fun_05:(NSDictionary *)args
{
return @"Fun:OC_Fun_05";
}
- (NSString *)OC_Fun_06:(NSDictionary *)args
{
return @"Fun:OC_Fun_06";
}