WkWebView是IOS8中引入的新組件,蘋果將UIWebViewDelegate 與 UIWebView 重構(gòu)成了 14 個(gè)類和 3 個(gè)協(xié)議并引入了不少新的功能和接口。由于一直以來蘋果對(duì)于WebView內(nèi)核封鎖的程度是令人發(fā)指的,WkWebView的引入無疑是另廣大開發(fā)者興奮的事
1、WKWebView介紹
WKWebView是現(xiàn)代 WebKit API 在 iOS 8 和 OS X Yosemite 應(yīng)用中的核心部分。它代替了 UIKit 中的UIWebView和 AppKit 中的WebView,提供了統(tǒng)一的跨雙平臺(tái) API(iOS和OS)。
2、WKWebView新特性
- 在性能、穩(wěn)定性、功能方面有很大提升(最直觀的體現(xiàn)就是加載網(wǎng)頁是占用的內(nèi)存,模擬器加載百度與開源中國網(wǎng)站時(shí),WKWebView占用23M,而UIWebView占用85M)
- 和 Safari 相同的 JavaScript 引擎,允許JavaScript的Nitro庫加載并使用(UIWebView中限制);
- 支持了更多的HTML5特性;
- 自詡擁有 60fps 滾動(dòng)刷新率、內(nèi)置手勢(shì)、高效的 app 和 web 信息交換通道
3、WKWebView使用
-
在WKWebView,一般都是使用UIWebView,而UIWebView的問題主要有:
- 內(nèi)存占用大
- 效率低
- 與JS交互比較麻煩
- 可擴(kuò)展性低等
使用WKWebView可以有效地解決上述問題。
示例:
// 創(chuàng)建webview
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
// 創(chuàng)建請(qǐng)求
NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
// 加載網(wǎng)頁
[webView loadRequest:request];
// 將webView添加到界面
[self.view addSubview:webView];
- WKWebView操作JS
- WKWebView加載JS
//JS文件路徑
NSString *jsPath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
//讀取JS文件內(nèi)容
NSString *jsContent = [NSString stringWithContentsOfFile:jsPath encoding:NSUTF8StringEncoding error:nil];
//創(chuàng)建用戶腳本對(duì)象,
//WKUserScriptInjectionTimeAtDocumentStart :HTML文檔創(chuàng)建后,完成加載前注入,類似于<head>中
//WKUserScriptInjectionTimeAtDocumentEnd :HTML文件完成加載后注入,類似于<body>中
WKUserScript *script = [[WKUserScript alloc] initWithSource:jsContent injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
//添加用戶腳本
[webView.configuration.userContentController addUserScript:script];
- WKWebView執(zhí)行JS方法
//執(zhí)行JS方法
[webView evaluateJavaScript:@"test()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
//result為執(zhí)行js方法的返回值
if(error){
NSLog(@"Success");
}else{
NSLog(@"Fail");
}
}];
##4、WKWebView代理方法
- WKNavigationDelegate 協(xié)議
pragma mark - WKNavigationDelegate
// 頁面開始加載時(shí)調(diào)用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
}
// 內(nèi)容開始返回時(shí)調(diào)用 - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
}
// 頁面加載完成時(shí)調(diào)用 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
}
// 頁面加載失敗時(shí)調(diào)用 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation {
}新增的三個(gè)代理方法:
// 這個(gè)方法是服務(wù)器重定向時(shí)調(diào)用,即 接收到服務(wù)器跳轉(zhuǎn)請(qǐng)求之后調(diào)用
(void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
}
// 在收到響應(yīng)后,決定是否跳轉(zhuǎn)(void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
}
// 在發(fā)送請(qǐng)求之前,決定是否跳轉(zhuǎn)-
(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
//需執(zhí)行decisionHandler的block。
} -
WKUIDelegate 協(xié)議
#pragma mark - WKUIDelegate /// 創(chuàng)建一個(gè)新的WebView - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures { return nil; } /** * web界面中有彈出警告框時(shí)調(diào)用 * * @param webView 實(shí)現(xiàn)該代理的webview * @param message 警告框中的內(nèi)容 * @param frame 主窗口 * @param completionHandler 警告框消失調(diào)用 */ - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void (^)())completionHandler { } /// 輸入框 - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler { } /// 確認(rèn)框 - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler { } /// 警告框 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler { }
5、WKWebView疑難
-
WKWebView加載POST請(qǐng)求無法發(fā)送參數(shù)問題
- 使用NSURLSession發(fā)送一個(gè)請(qǐng)求,然后把請(qǐng)求下來的數(shù)據(jù)當(dāng)作本地HTML加載
// 創(chuàng)建WKWebView WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds]; // 將WKWebView添加到當(dāng)前View [self.view addSubview:webView]; // 設(shè)置訪問的URL NSURL *url = [NSURL URLWithString:@"http://www.example.com"]; // 根據(jù)URL創(chuàng)建請(qǐng)求 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // 設(shè)置請(qǐng)求方法為POST [request setHTTPMethod:@"POST"]; // 設(shè)置請(qǐng)求參數(shù) [request setHTTPBody:[@"username=aaa&password=123" dataUsingEncoding:NSUTF8StringEncoding]]; // 實(shí)例化網(wǎng)絡(luò)會(huì)話 NSURLSession *session = [NSURLSession sharedSession]; // 創(chuàng)建請(qǐng)求Task NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { // 將請(qǐng)求到的網(wǎng)頁數(shù)據(jù)用loadHTMLString 的方法加載 NSString *htmlStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; [webView loadHTMLString:htmlStr baseURL:nil]; }]; // 開啟網(wǎng)絡(luò)任務(wù) [task resume];
- 使用JavaScript解決WKWebView無法發(fā)送POST參數(shù)問題(iOS8)
-
iOS9以前版本讀取本地HTML的問題
當(dāng)使用loadRequest來讀取本地的HTML時(shí),WKWebView是無法讀取成功的,后臺(tái)會(huì)出現(xiàn)如下的提示:
Could not create a sandbox extension for /
原因是WKWebView是不允許通過loadRequest的方法來加載本地根目錄的HTML文件。
而在iOS9的SDK中加入了以下方法來加載本地的HTML文件:
[WKWebView loadFileURL:allowingReadAccessToURL:]
但是在iOS9以下的版本是沒提供這個(gè)便利的方法的。以下為解決方案的思路,就是在iOS9以下版本時(shí),先將本地HTML文件的數(shù)據(jù)copy到tmp目錄中,然后再使用loadRequest來加載。但是如果在HTML中加入了其他資源文件,例如js,css,image等必須一同copy到temp中。這個(gè)是最蛋疼的事情了。//將文件copy到tmp目錄 - (NSURL *)fileURLForBuggyWKWebView8:(NSURL *)fileURL { NSError *error = nil; if (!fileURL.fileURL || ![fileURL checkResourceIsReachableAndReturnError:&error]) { return nil; } // Create "/temp/www" directory NSFileManager *fileManager= [NSFileManager defaultManager]; NSURL *temDirURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:@"www"]; [fileManager createDirectoryAtURL:temDirURL withIntermediateDirectories:YES attributes:nil error:&error]; NSURL *dstURL = [temDirURL URLByAppendingPathComponent:fileURL.lastPathComponent]; // Now copy given file to the temp directory [fileManager removeItemAtURL:dstURL error:&error]; [fileManager copyItemAtURL:fileURL toURL:dstURL error:&error]; // Files in "/temp/www" load flawlesly :) return dstURL; } //調(diào)用邏輯 NSString *path = [[NSBundle mainBundle] pathForResource:@"indexoff" ofType:@"html"]; if(path){ if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) { // iOS9. One year later things are OK. NSURL *fileURL = [NSURL fileURLWithPath:path]; [self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL]; } else { // iOS8. Things can be workaround-ed // Brave people can do just this // fileURL = try! pathForBuggyWKWebView8(fileURL) // webView.loadRequest(NSURLRequest(URL: fileURL)) NSURL *fileURL = [self.fileHelper fileURLForBuggyWKWebView8:[NSURL fileURLWithPath:path]]; NSURLRequest *request = [NSURLRequest requestWithURL:fileURL]; [self.webView loadRequest:request]; } }
問題:http://stackoverflow.com/questions/24882834/wkwebview-not-loading-local-files-under-ios-8
具體參見:https://github.com/shazron/WKWebViewFIleUrlTest
ps:在實(shí)際測(cè)試中,上述方法在iOS8.0系統(tǒng)中也無法成功。如果在iOS8.0系統(tǒng)有成功的請(qǐng)告知下。謝謝!