雖然WKWebView是在Apple的WWDC 2014隨iOS 8和OS X 10.10出來(lái)的,是為了解決UIWebView加載速度慢谒所、占用內(nèi)存大的問(wèn)題沛申。但是由于之前還要適配iOS7庶弃,所以就沒有使用“鹪欤現(xiàn)在項(xiàng)目都適配iOS 8以上了忽肛,所以就開始使用WKWebView了烂斋,但是發(fā)現(xiàn)在使用的時(shí)候有好多坑屹逛,希望這篇文章能帶大家繞過(guò)坑,更好的使用WKWebView帘瞭。
這篇文章主要介紹了以下問(wèn)題淑掌,方便小伙伴們查閱:
- WKWebView的基本介紹和使用
- WKWebView和JavaScript的交互
- WKWebView 默認(rèn)不彈出js的alert問(wèn)題
- WKWebView 默認(rèn)是不能識(shí)別電話號(hào)碼
- WKWebView 攔截js通過(guò)window.open() 打開的窗口
- WKWebView解決文字顯示太小問(wèn)題
下面開始說(shuō)第一個(gè)問(wèn)題** WKWebView**基本使用
1.創(chuàng)建 跟UIWebview 一樣
// 創(chuàng)建WKWebView
WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
// 設(shè)置訪問(wèn)的URL
NSURL *url = [NSURL URLWithString:@"http://www.reibang.com"];
// 根據(jù)URL創(chuàng)建請(qǐng)求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// WKWebView加載請(qǐng)求
[webView loadRequest:request];
// 將WKWebView添加到視圖
[self.view addSubview:webView];
UIWebView和WKWebView的代理方法做一個(gè)對(duì)比
1.準(zhǔn)備加載頁(yè)面
UIWebViewDelegate: - webView:shouldStartLoadWithRequest:navigationType
WKNavigationDelegate: - webView:didStartProvisionalNavigation:
2.內(nèi)容開始加載
UIWebViewDelegate: - webViewDidStartLoad:
WKNavigationDelegate: - webView:didCommitNavigation:
3.頁(yè)面加載完成
UIWebViewDelegate: - webViewDidFinishLoad:
WKNavigationDelegate: - webView:didFinishNavigation:
4.頁(yè)面加載失敗
UIWebViewDelegate: - webView:didFailLoadWithError:
WKNavigationDelegate: - webView:didFailNavigation:withError:
WKNavigationDelegate: - webView:didFailProvisionalNavigation:withError:
可以看到很簡(jiǎn)單,和UIWebView并沒有多少差別蝶念,然而性能就刷刷刷的提上去了抛腕,是不是很爽呢?如果你只是簡(jiǎn)單的集成個(gè)Web頁(yè)到App媒殉,這些已經(jīng)夠了担敌。不過(guò)很多時(shí)候并沒有那么簡(jiǎn)單,還需要處理各種東西适袜,那么接著往后看柄错。
接下來(lái)我們開始說(shuō)第二個(gè)問(wèn)題 WKWebView和JavaScript的交互
WKWebView和JavaScript的交互主要涉及到兩個(gè)方面,一個(gè)是OC調(diào)用JavaScript ,另一個(gè)是 JavaScript 調(diào)用OC的方法,
在WebKit框架中,有WKWebView可以替換UIKit的UIWebView和AppKit的WebView苦酱,而且提供了在兩個(gè)平臺(tái)可以一致使用的接口售貌。WebKit框架使得開發(fā)者可以在原生App中使用Nitro來(lái)提高網(wǎng)頁(yè)的性能和表現(xiàn),Nitro就是Safari的JavaScript引擎,WKWebView不支持JavaScriptCore的方式但提供message handler的方式為JavaScript與Native通信疫萤。(這個(gè)引自天狐博客颂跨,更多的與UIWebView或者WKWebView的交互方法可以在這里看到)。
1. OC調(diào)用JavaScript
OC調(diào)用JavaScrippt是相對(duì)來(lái)說(shuō)比較簡(jiǎn)單的
只需要在調(diào)用的地方添加下面一句代碼即可
//showAlert()是js里面的方法,這樣就可以實(shí)現(xiàn)調(diào)用js方法
[self.webView evaluateJavaScript:@"showAlert('傳參')" completionHandler:^(id item, NSError * _Nullable error) {
// Block中處理是否通過(guò)了或者執(zhí)行JS錯(cuò)誤的代碼
}];
2. JavaScript 調(diào)用OC的方法,相對(duì)來(lái)說(shuō)復(fù)雜一點(diǎn)
這地方需要兩個(gè)配置,一個(gè)是OC代碼的配置,另一個(gè)是JS代碼的配置,下面先說(shuō)一下OC代碼的配置,細(xì)心的小伙伴可能已經(jīng)發(fā)現(xiàn)了扯饶,創(chuàng)建WKWebView的時(shí)候恒削,除了有- initWithFrame:方法外池颈,還有一個(gè)高端的方法:- initWithFrame:configuration:方法。
#######OC代碼的配置
1.配置 WKWebView
// 創(chuàng)建配置
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
// 創(chuàng)建UserContentController(提供JavaScript向webView發(fā)送消息的方法)
WKUserContentController* userContent = [[WKUserContentController alloc] init];
// 添加消息處理钓丰,注意:self指代的對(duì)象需要遵守WKScriptMessageHandler協(xié)議躯砰,結(jié)束時(shí)需要移除
//NativeMethod 這個(gè)方法一會(huì)要與JS里面的方法寫的一樣
[userContent addScriptMessageHandler:self name:@"NativeMethod"];
// 將UserConttentController設(shè)置到配置文件
config.userContentController = userContent;
// 高端的自定義配置創(chuàng)建WKWebView
WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds configuration:config];
// 設(shè)置訪問(wèn)的URL
NSURL *url = [NSURL URLWithString:@"http://www.reibang.com"];
// 根據(jù)URL創(chuàng)建請(qǐng)求
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// WKWebView加載請(qǐng)求
[webView loadRequest:request];
// 將WKWebView添加到視圖
[self.view addSubview:webView];
2.實(shí)現(xiàn)協(xié)議方法
好了,現(xiàn)在萬(wàn)事俱備携丁,只欠東風(fēng)了琢歇。東風(fēng)是什么呢,就是該在哪兒處理梦鉴±蠲#可以看到WKScriptMessageHandler的協(xié)議里面只有一個(gè)方法,就是:
- userContentController:didReceiveScriptMessage:
相信聰明的你已經(jīng)猜到了肥橙。是的魄宏,就是在這個(gè)代理方法里面操作:如果JavaScript執(zhí)行已經(jīng)寫好的:window.webkit.messageHandlers.NativeMethod.postMessage("就是一個(gè)消息啊");這行代碼,這個(gè)代理方法就會(huì)走存筏,并且會(huì)有個(gè)WKScriptMessage的對(duì)象宠互,這個(gè)WKScriptMessage對(duì)象有個(gè)name屬性,拿到之后你會(huì)發(fā)現(xiàn)方篮,就是我們注冊(cè)的NativeMethod這個(gè)字符串名秀,這時(shí)候你就可以手動(dòng)調(diào)用Native的方法了。如果有多個(gè)方法需要調(diào)用的話怎么辦藕溅,看到JavaScript中postMessage()方法有一個(gè)參數(shù)了沒有匕得,可以根據(jù)這里的參數(shù)來(lái)區(qū)分調(diào)用原生App的哪個(gè)方法。
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
// 判斷是否是調(diào)用原生的
if ([@"NativeMethod" isEqualToString:message.name]) {
// 判斷message的內(nèi)容巾表,然后做相應(yīng)的操作
if ([@"close" isEqualToString:message.body]) {
}
}
}
2.JavaScript的配置
JavaScript調(diào)用Native的方法就需要前端和Native的小伙伴們配合了汁掠,需要前端的小伙伴在JS的方法中調(diào)用:
window.webkit.messageHandlers.方法名.postMessage(參數(shù))
注意:
參數(shù)沒有時(shí)傳 (null) 這里是個(gè)坑點(diǎn)
第一:實(shí)現(xiàn)以上代碼的時(shí)候不要忘記實(shí)現(xiàn)** WKScriptMessageHandler**協(xié)議
第二:上面將當(dāng)前ViewController設(shè)置為MessageHandler之后需要在當(dāng)前ViewController銷毀前將其移除(dealloc方法),否則會(huì)造成內(nèi)存泄漏集币。
//頁(yè)面進(jìn)入時(shí)創(chuàng)建
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[self.webView.configuration.userContentController addScriptMessageHandler:self name:JS_goPageSelectClass];
[self.webView.configuration.userContentController addScriptMessageHandler:self name:JS_goClasscardList];
}
//頁(yè)面消失是移除
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:JS_goPageSelectClass];
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:JS_goClasscardList];
}
js區(qū)分Android和iOS的方法
var u = navigator.userAgent;
var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android終端
var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //iOS終端
if(isAndroid){
window.Android.alipayOrder();
}
if(isiOS){
window.webkit.messageHandlers.alipayOrder.postMessage(r);
}
第三個(gè)問(wèn)題 WKWebview 默認(rèn)是不彈出js的alert 要想可以彈出alert 需要手動(dòng)的設(shè)置代理實(shí)現(xiàn)
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
協(xié)議方法
具體的實(shí)現(xiàn)方法是,我們采用源生的UIAlertController 來(lái)實(shí)現(xiàn)彈出框,獲取js里面的alert內(nèi)容顯示出來(lái)
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
NSLog(@"點(diǎn)擊了取消按鈕==%@",message);
}])];
[alertController addAction:([UIAlertAction actionWithTitle:@"確認(rèn)" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler();
NSLog(@"點(diǎn)擊了確定按鈕==%@",message);
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
問(wèn)題四 : WKWebview 默認(rèn)是不能識(shí)別電話號(hào)的,這里需要通過(guò)實(shí)現(xiàn)一個(gè)協(xié)議來(lái)實(shí)現(xiàn)撥打電話的功能
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
具體的代碼如下:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler{
NSURL *URL = navigationAction.request.URL;
NSLog(@"獲取到URL========%@",URL);
NSString *scheme = [URL scheme];
UIApplication *app = [UIApplication sharedApplication];
// 打電話
if ([scheme isEqualToString:@"tel"]) {
if ([app canOpenURL:URL]) {
[app openURL:URL];
// 一定要加上這句,否則會(huì)打開新頁(yè)面
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
}
decisionHandler(WKNavigationActionPolicyAllow);
}
注:對(duì)應(yīng)html代碼采用的是a標(biāo)簽
<a href="tel:18158711698">識(shí)別電話號(hào)碼18158711698,進(jìn)行撥打電話</a>
問(wèn)題五: WKWebView 默認(rèn)攔截open.window() 打開新的頁(yè)面
- (WKWebView )webView:(WKWebView )webView createWebViewWithConfiguration:(WKWebViewConfiguration )configuration forNavigationAction:(WKNavigationAction )navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
會(huì)攔截到window.open()事件.
只需要我們?cè)谠诜椒▋?nèi)進(jìn)行處理
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
問(wèn)題六:WKWebView解決顯示字體太小的問(wèn)題
在使用WKWebView的時(shí)候考阱,常常會(huì)碰到顯示內(nèi)容比實(shí)際css設(shè)置的樣式不能正常顯示,內(nèi)容普遍的偏小鞠苟。其實(shí)導(dǎo)致這樣問(wèn)題的根源是少了HTML5的meta標(biāo)簽乞榨。解決的辦法可以在iOS端添加以下的內(nèi)容,當(dāng)然也可以讓后臺(tái)添加完整的HTML5的格式当娱。如果要在iOS端指定字體的大小也是可以的(不推薦在客戶端設(shè)置字體大谐约取)。
NSString *jScript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
WKUserContentController *wkUController = [[WKUserContentController alloc] init];
[wkUController addUserScript:wkUScript];
WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init];
wkWebConfig.userContentController = wkUController;
_myWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0,CGRectGetMaxY(headerView.frame)+10, M_S.width,M_S.height - CGRectGetMaxY(headerView.frame) - 40) configuration:wkWebConfig];
客戶端設(shè)置字體大小eg:
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation{
//修改字體大小 300%
[ webView evaluateJavaScript:@"document.getElementsByTagName('body')[0].style.webkitTextSizeAdjust= '200%'" completionHandler:nil];
// //修改字體顏色 #9098b8
// [ webView evaluateJavaScript:@"document.getElementsByTagName('body')[0].style.webkitTextFillColor= '#9098b8'" completionHandler:nil];
}