系列文章:
一. native干預(yù)webView
- native干預(yù)webView交互方式主要是三種:
1.通過webView代理方法,攔截webView加載過程中的信息
2.native向HTML注入腳本
3.native直接調(diào)用HTML中的JS
1. 攔截webView發(fā)的出請求
在webView各個階段的代理回調(diào)中攔截請求,然后根據(jù)請求做出判斷,進(jìn)行處理。比如繼續(xù)請求或者停止請求。這里就不細(xì)說了。
2. native向HTML注入腳本

從WWDC課件上可以看到,在WKWebView中,我們有
User Script和Script Messages兩種方對網(wǎng)頁內(nèi)容進(jìn)行控制。使用這兩種方式進(jìn)行操作的話,都會使用到WKUserContentController這個類。
2.1. User Script
User Script的意思是被網(wǎng)頁接受的用戶腳本。也就是你寫到網(wǎng)頁中的腳本必須是可以運行的。
腳本的作用:
- 更新document
- 監(jiān)聽js事件
- 加載資源
- 與App進(jìn)行溝通
腳本的運行時機:
- HTML中document 開始時
- HTML中document 結(jié)束時
腳本的運行的位置:
- 全部的frame中
- 只在主frame中
設(shè)置方法如下:
- (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly;
舉例:比如在webView中彈出一個提示框,顯示的內(nèi)容是document.cookie中的Cookies,代碼如下:
WKWebViewConfiguration * webConfiguration = [[WKWebViewConfiguration alloc]init];
WKUserContentController *contentController = [[WKUserContentController alloc] init];
WKUserScript *alertCookieScript = [[WKUserScript alloc] initWithSource:@"alert(document.cookie);" injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:NO]; //傳入js腳本
[contentController addUserScript:alertCookieScript];
webConfiguration.userContentController = contentController;
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:webConfiguration];
2.2. Script Messages
上邊的例子,我們的腳本只是簡單的讓HTML顯示一個alert。假如我們向網(wǎng)頁注入用戶腳本后,是想要反向獲得用戶腳本執(zhí)行的結(jié)果或者HTML中某些元素的值,就用到Script Messages。WebKit提供了WKScriptMessageHandler類,通過注冊監(jiān)聽的方式來接收HTML的回調(diào),得到我們想要的。
WKScriptMessageHandler的作用:
- 網(wǎng)頁可以發(fā)送任意的message
- 網(wǎng)頁和應(yīng)用交互
- 處理無效的請求
舉例:獲取儲存在document.cookie中的Cookies。代碼邏輯分為三步,如下:
第一步,先想網(wǎng)頁中注冊用戶腳本,作用是當(dāng)document.cookie中有Cookies的時候,就主動發(fā)送消息,這里我們用到User Script:
WKUserScript *cookieScript = [[WKUserScript alloc] initWithSource:@"window.webkit.messageHandlers.currentCookies.postMessage(document.cookie);"" injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:NO]; [contentController addUserScript:cookieScript]; webConfiguration.userContentController = contentController;然后,注冊WKScriptMessageHandler:
// 注冊監(jiān)聽對象 WKWebViewConfiguration * webConfiguration = [[WKWebViewConfiguration alloc]init]; [configuration.userContentController addScriptMessageHandler:handler name:@“currentCookies"]; // 回調(diào) - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { if ([message.name isEqualToString:@"currentCookies"]) { NSString *cookiesStr = message.body; NSLog(@"當(dāng)前的cookie為: %@", cookiesStr); } }最后,移除注冊:
[userContentController removeScriptMessageHandlerForName:@“currentCookies"];
3.native直接調(diào)用HTML中的JS代碼
直接有API提供的方法可以實現(xiàn)。和UIWebView的區(qū)別在于,UIWebView中此方法是同步的,而在WKWebView是異步執(zhí)行的。這一點需要特別注意。 具體方法如下:
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
其中:
- javaScriptString 是要執(zhí)行的JS代碼
- completionHandler 是前一個JS代碼的返回結(jié)果,注意block中第一個參數(shù)是id,具體值是根據(jù)你寫得JS變化的。
舉例:獲取HTML中名字叫meta的標(biāo)簽的長度
- (void)ocCallJavaScript:(void(^)(void))complete
{
[self.webView evaluateJavaScript:@"document.getElementsByTagName('meta').length"
completionHandler:^(NSString *count, NSError * _Nullable error)
{
!complete ? complete();
}];
}
二. webView調(diào)用OC代碼
主要是WKUIDelegate的相關(guān)代理方法。在UIWebView中,新打開一個webView、alert提示框都是由webView自己控制,現(xiàn)在WKWebView交給開發(fā)者自己處理。其中,關(guān)于_blank的處理,我們放到《WKWebView項目實踐分享(六)- 項目實踐:User Agent、跨域、重定向及其它》中詳細(xì)來說。
#pragma mark - WKUIDelegate
- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
// 重新打開一個webView,需要 處理_blank
WKFrameInfo *frameInfo = navigationAction.targetFrame;
if(frameInfo == nil || frameInfo.isMainFrame == NO){
[webView loadRequest:navigationAction.request];
}
return nil;
}
- (void)webViewDidClose:(WKWebView *)webView
{
}
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
{
[LLAlertController showAlertWithTitle:@"溫馨提示"
message:message?:@""
cancelActionTitle:nil
otherActionTitles:@[@"確定"]
showInController:self
actionOnClick:^(UIAlertController * _Nullable alertController, NSInteger actionIndex, NSString * _Nullable actionTitle)
{
completionHandler();
}];
}
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler
{
[LLAlertController showAlertWithTitle:@"溫馨提示"
message:message?:@""
cancelActionTitle:@"取消"
otherActionTitles:@[@"確定"]
showInController:self
actionOnClick:^(UIAlertController * _Nullable alertController, NSInteger actionIndex, NSString * _Nullable actionTitle)
{
if (actionIndex == -1) {
completionHandler(NO);
}
else{
completionHandler(YES);
}
}];
}
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable result))completionHandler
{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:defaultText message:@"JS調(diào)用輸入框" preferredStyle:UIAlertControllerStyleAlert];
[alert addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.textColor = [UIColor redColor];
}];
[alert addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler([[alert.textFields lastObject] text]);
}]];
[self presentViewController:alert animated:YES completion:NULL];
}
參考
交流

希望能和大家交流技術(shù)
Blog:http://www.lilongcnc.cc