系列文章:
一. native干預webView
- native干預webView交互方式主要是三種:
1.通過webView代理方法,攔截webView加載過程中的信息
2.native向HTML注入腳本
3.native直接調用HTML中的JS
1. 攔截webView發的出請求
在webView各個階段的代理回調中攔截請求,然后根據請求做出判斷,進行處理。比如繼續請求或者停止請求。這里就不細說了。
2. native向HTML注入腳本
從WWDC課件上可以看到,在WKWebView中,我們有
User Script
和Script Messages
兩種方對網頁內容進行控制。使用這兩種方式進行操作的話,都會使用到WKUserContentController這個類。
2.1. User Script
User Script的意思是被網頁接受的用戶腳本。也就是你寫到網頁中的腳本必須是可以運行的。
腳本的作用:
- 更新document
- 監聽js事件
- 加載資源
- 與App進行溝通
腳本的運行時機:
- HTML中document 開始時
- HTML中document 結束時
腳本的運行的位置:
- 全部的frame中
- 只在主frame中
設置方法如下:
- (instancetype)initWithSource:(NSString *)source injectionTime:(WKUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly;
舉例:比如在webView中彈出一個提示框,顯示的內容是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。假如我們向網頁注入用戶腳本后,是想要反向獲得用戶腳本執行的結果或者HTML中某些元素的值,就用到Script Messages
。WebKit提供了WKScriptMessageHandler
類,通過注冊監聽的方式來接收HTML的回調,得到我們想要的。
WKScriptMessageHandler
的作用:
- 網頁可以發送任意的message
- 網頁和應用交互
- 處理無效的請求
舉例:獲取儲存在document.cookie中的Cookies。代碼邏輯分為三步,如下:
第一步,先想網頁中注冊用戶腳本,作用是當document.cookie中有Cookies的時候,就主動發送消息,這里我們用到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:
// 注冊監聽對象 WKWebViewConfiguration * webConfiguration = [[WKWebViewConfiguration alloc]init]; [configuration.userContentController addScriptMessageHandler:handler name:@“currentCookies"]; // 回調 - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message { if ([message.name isEqualToString:@"currentCookies"]) { NSString *cookiesStr = message.body; NSLog(@"當前的cookie為: %@", cookiesStr); } }
最后,移除注冊:
[userContentController removeScriptMessageHandlerForName:@“currentCookies"];
3.native直接調用HTML中的JS代碼
直接有API提供的方法可以實現。和UIWebView的區別在于,UIWebView中此方法是同步的,而在WKWebView是異步執行的。這一點需要特別注意。 具體方法如下:
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^ _Nullable)(_Nullable id, NSError * _Nullable error))completionHandler;
其中:
- javaScriptString 是要執行的JS代碼
- completionHandler 是前一個JS代碼的返回結果,注意block中第一個參數是id,具體值是根據你寫得JS變化的。
舉例:獲取HTML中名字叫meta
的標簽的長度
- (void)ocCallJavaScript:(void(^)(void))complete
{
[self.webView evaluateJavaScript:@"document.getElementsByTagName('meta').length"
completionHandler:^(NSString *count, NSError * _Nullable error)
{
!complete ? complete();
}];
}
二. webView調用OC代碼
主要是WKUIDelegate的相關代理方法。在UIWebView中,新打開一個webView、alert提示框都是由webView自己控制,現在WKWebView交給開發者自己處理。其中,關于_blank
的處理,我們放到《WKWebView項目實踐分享(六)- 項目實踐:User Agent、跨域、重定向及其它》中詳細來說。
#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調用輸入框" 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];
}
參考
交流
希望能和大家交流技術
Blog:http://www.lilongcnc.cc