完全抄錄:
iOS中UIWebView與WKWebView、JavaScript與OC交互、Cookie管理看我就夠(上)
深入淺出 JavaScriptCore
JavaScriptCore框架詳解
UIWebView OC調(diào)用JS
1、stringByEvaluatingJavaScriptFromString
/*缺點(diǎn)
1、所以無法判斷是否調(diào)用成功,失敗返回nil,js本身方法返回是nil
2、返回值是NSString,其他類型數(shù)據(jù)需要轉(zhuǎn)換解析
*/
- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
self.navigationItem.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
- JavaScriptCore(iOS 7.0 +)
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//更新標(biāo)題,這是上面的講過的方法
//self.navigationItem.title = [webView stringByEvaluatingJavaScriptFromString:@"document.title"];
//獲取該UIWebView的javascript上下文
JSContext *jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//這也是一種獲取標(biāo)題的方法。
JSValue *value = [self.jsContext evaluateScript:@"document.title"];
//更新標(biāo)題
self.navigationItem.title = value.toString;
}
//在調(diào)用前,設(shè)置異常回調(diào)
[self.jsContext setExceptionHandler:^(JSContext *context, JSValue *exception){
NSLog(@"%@", exception);
}];
//執(zhí)行方法
JSValue *value = [self.jsContext evaluateScript:@"document.titlexxxx"];
UIWebView JS調(diào)用OC
- Custom URL Scheme(攔截URL)
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
//標(biāo)準(zhǔn)的URL包含scheme、host、port、path、query、fragment等
NSURL *URL = request.URL;
if ([URL.scheme isEqualToString:@"darkangel"]) {
if ([URL.host isEqualToString:@"smsLogin"]) {
NSLog(@"短信驗(yàn)證碼登錄,參數(shù)為 %@", URL.query);
return NO;// NO(阻止本次跳轉(zhuǎn))
}
}
return YES;
}
優(yōu)點(diǎn):泛用性強(qiáng),可以配合h5實(shí)現(xiàn)頁(yè)面動(dòng)態(tài)化。比如頁(yè)面中一個(gè)活動(dòng)鏈接到活動(dòng)詳情頁(yè),當(dāng)native尚未開發(fā)完畢時(shí),鏈接可以是一個(gè)h5鏈接,等到native開發(fā)完畢時(shí),可以通過該方法跳轉(zhuǎn)到native頁(yè)面,實(shí)現(xiàn)頁(yè)面動(dòng)態(tài)化。且該方案適用于Android和iOS,泛用性很強(qiáng)。
缺點(diǎn):無法直接獲取本次交互的返回值,比較適合單向傳參,且不關(guān)心回調(diào)的情景,比如h5頁(yè)面跳轉(zhuǎn)到native頁(yè)面等。
- JavaScriptCore(iOS 7.0 +)
OC現(xiàn)實(shí)JS的方法
JS代碼
function share(title, imgUrl, link) {
//這里需要OC實(shí)現(xiàn)
}
OC代碼
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//將js的function映射到OC的方法
[self convertJSFunctionsToOCMethods];
}
- (void)convertJSFunctionsToOCMethods
{
//獲取該UIWebview的javascript上下文
//self持有jsContext
self.jsContext = [self.webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
//js調(diào)用oc
//其中share就是js的方法名稱,賦給是一個(gè)block 里面是oc代碼
//此方法最終將打印出所有接收到的參數(shù),js參數(shù)是不固定的
// ja的"share",有則替換,無則添加。
self.jsContext[@"share"] = ^() {
NSArray *args = [JSContext currentArguments];//獲取到share里的所有參數(shù)
//args中的元素是JSValue,需要轉(zhuǎn)成OC的對(duì)象
NSMutableArray *messages = [NSMutableArray array];
for (JSValue *obj in args) {
[messages addObject:[obj toObject]];
}
NSLog(@"點(diǎn)擊分享js傳回的參數(shù):\n%@", messages);
};
}
一個(gè)有兩個(gè)參數(shù),一個(gè)返回值的js方法
JS
//該方法傳入兩個(gè)整數(shù),求和,并返回結(jié)果
function testAddMethod(a, b) {
//需要OC實(shí)現(xiàn)a+b,并返回
return a + b;
}
console.log(testAddMethod(1, 5)); //output 6
OC直接替換該方法
self.jsContext[@"testAddMethod"] = ^NSInteger(NSInteger a, NSInteger b) {
return a + b;
};
JS調(diào)用
console.log(testAddMethod(1, 5)); //output 6, 方法為 a + b
OC替換該方法為兩數(shù)相乘
self.jsContext[@"testAddMethod"] = ^NSInteger(NSInteger a, NSInteger b) {
return a * b;
};
JS調(diào)用
console.log(testAddMethod(1, 5)); //output 5,該方法變?yōu)榱?a * b。
OC調(diào)用方法原實(shí)現(xiàn),并且在原結(jié)果上乘以10
//調(diào)用方法的本來實(shí)現(xiàn),給原結(jié)果乘以10
JSValue *value = self.jsContext[@"testAddMethod"];
self.jsContext[@"testAddMethod"] = ^NSInteger(NSInteger a, NSInteger b) {
JSValue *resultValue = [value callWithArguments:[JSContext currentArguments]];
return resultValue.toInt32 * 10;
};
JS調(diào)用
console.log(testAddMethod(1, 5)); //output 60,該方法變?yōu)榱?a + b) * 10
回調(diào):
比如h5中有一個(gè)分享按鈕,用戶點(diǎn)擊之后,調(diào)用native分享(微信分享、微博分享等),在native分享成功或者失敗時(shí),回調(diào)h5頁(yè)面,告訴其分享結(jié)果,h5頁(yè)面刷新對(duì)應(yīng)的UI,顯示分享成功或者失敗
JS代碼
//聲明(其實(shí)不需要聲明,因?yàn)樵⑷氪a方法是:有則替換,無則添加)
function share(shareData) {
}
//調(diào)用的時(shí)候需要這么寫
share({
title: "title",
imgUrl: "http://img.dd.com/xxx.png",
link: location.href,
result: function(res) { //函數(shù)作為參數(shù)
console.log(res ? "success" : "failure");
}
});
OC代碼
//異步回調(diào)
self.jsContext[@"share"] = ^(JSValue *shareData) { //首先這里要注意,回調(diào)的參數(shù)不能直接寫NSDictionary類型,為何呢?
//仔細(xì)看,打印出的確實(shí)是一個(gè)NSDictionary,但是result字段對(duì)應(yīng)的不是block而是一個(gè)NSDictionary
NSLog(@"%@", [shareData toObject]);
//獲取shareData對(duì)象的result屬性,這個(gè)JSValue對(duì)應(yīng)的其實(shí)是一個(gè)javascript的function。
JSValue *resultFunction = [shareData valueForProperty:@"result"];
//回調(diào)block,將js的function轉(zhuǎn)換為OC的block
void (^result)(BOOL) = ^(BOOL isSuccess) {
[resultFunction callWithArguments:@[@(isSuccess)]];
};
//模擬異步回調(diào)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"回調(diào)分享成功");
result(YES);
});
};
UIWebView的Cookie管理
UIWebView的Cookie管理很簡(jiǎn)單,一般不需要我們手動(dòng)操作Cookie,因?yàn)樗蠧ookie都會(huì)被[NSHTTPCookieStorage sharedHTTPCookieStorage]這個(gè)單例管理,而且UIWebView會(huì)自動(dòng)同步CookieStorage中的Cookie,所以只要我們?cè)贜ative端,正常登陸退出,h5在適當(dāng)時(shí)候刷新,就可以正確的維持登錄狀態(tài),不需要做多余的操作。
可能有一些情況下,我們需要在訪問某個(gè)鏈接時(shí),添加一個(gè)固定Cookie用來做區(qū)分,那么就可以通過header來實(shí)現(xiàn)
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
[request addValue:@"customCookieName=1314521;" forHTTPHeaderField:@"Set-Cookie"];
[self.webView loadRequest:request];
也可以主動(dòng)操作NSHTTPCookieStorage,添加一個(gè)自定義Cookie
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:@{
NSHTTPCookieName: @"customCookieName",
NSHTTPCookieValue: @"1314521",
NSHTTPCookieDomain: @".baidu.com",
NSHTTPCookiePath: @"/"
}];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie]; //Cookie存在則覆蓋,不存在添加
還有一些常用的方法,如讀取所有Cookie轉(zhuǎn)換成HTTPHeaderFields,并添加到request的header中
NSArray *cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage].cookies;
//Cookies數(shù)組轉(zhuǎn)換為requestHeaderFields
NSDictionary *requestHeaderFields = [NSHTTPCookie requestHeaderFieldsWithCookies:cookies];
//設(shè)置請(qǐng)求頭
request.allHTTPHeaderFields = requestHeaderFields;