WkWebView是IOS8中引入的新组件,苹果将UIWebViewDelegate 与 UIWebView 重构成了 14 个类和 3 个协议并引入了不少新的功能和接口。由于一直以来苹果对于WebView内核封锁的程度是令人发指的,WkWebView的引入无疑是另广大开发者兴奋的事
1、WKWebView介绍
WKWebView是现代 WebKit API 在 iOS 8 和 OS X Yosemite 应用中的核心部分。它代替了 UIKit 中的UIWebView和 AppKit 中的WebView,提供了统一的跨双平台 API(iOS和OS)。
2、WKWebView新特性
- 在性能、稳定性、功能方面有很大提升(最直观的体现就是加载网页是占用的内存,模拟器加载百度与开源中国网站时,WKWebView占用23M,而UIWebView占用85M)
- 和 Safari 相同的 JavaScript 引擎,允许JavaScript的Nitro库加载并使用(UIWebView中限制);
- 支持了更多的HTML5特性;
- 自诩拥有 60fps 滚动刷新率、内置手势、高效的 app 和 web 信息交换通道
3、WKWebView使用
-
在WKWebView,一般都是使用UIWebView,而UIWebView的问题主要有:
- 内存占用大
- 效率低
- 与JS交互比较麻烦
- 可扩展性低等
使用WKWebView可以有效地解决上述问题。
示例:
// 创建webview
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
// 创建请求
NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
// 加载网页
[webView loadRequest:request];
// 将webView添加到界面
[self.view addSubview:webView];
- WKWebView操作JS
- WKWebView加载JS
//JS文件路径
NSString *jsPath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
//读取JS文件内容
NSString *jsContent = [NSString stringWithContentsOfFile:jsPath encoding:NSUTF8StringEncoding error:nil];
//创建用户脚本对象,
//WKUserScriptInjectionTimeAtDocumentStart :HTML文档创建后,完成加载前注入,类似于<head>中
//WKUserScriptInjectionTimeAtDocumentEnd :HTML文件完成加载后注入,类似于<body>中
WKUserScript *script = [[WKUserScript alloc] initWithSource:jsContent injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
//添加用户脚本
[webView.configuration.userContentController addUserScript:script];
- WKWebView执行JS方法
//执行JS方法
[webView evaluateJavaScript:@"test()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
//result为执行js方法的返回值
if(error){
NSLog(@"Success");
}else{
NSLog(@"Fail");
}
}];
##4、WKWebView代理方法
- WKNavigationDelegate 协议
pragma mark - WKNavigationDelegate
// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
}
// 内容开始返回时调用 - (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
}
// 页面加载完成时调用 - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
}
// 页面加载失败时调用 - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation {
}新增的三个代理方法:
// 这个方法是服务器重定向时调用,即 接收到服务器跳转请求之后调用
(void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
}
// 在收到响应后,决定是否跳转(void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
}
// 在发送请求之前,决定是否跳转-
(void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
//需执行decisionHandler的block。
} -
WKUIDelegate 协议
#pragma mark - WKUIDelegate /// 创建一个新的WebView - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures { return nil; } /** * web界面中有弹出警告框时调用 * * @param webView 实现该代理的webview * @param message 警告框中的内容 * @param frame 主窗口 * @param completionHandler 警告框消失调用 */ - (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 { } /// 确认框 - (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请求无法发送参数问题
- 使用NSURLSession发送一个请求,然后把请求下来的数据当作本地HTML加载
// 创建WKWebView WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds]; // 将WKWebView添加到当前View [self.view addSubview:webView]; // 设置访问的URL NSURL *url = [NSURL URLWithString:@"http://www.example.com"]; // 根据URL创建请求 NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // 设置请求方法为POST [request setHTTPMethod:@"POST"]; // 设置请求参数 [request setHTTPBody:[@"username=aaa&password=123" dataUsingEncoding:NSUTF8StringEncoding]]; // 实例化网络会话 NSURLSession *session = [NSURLSession sharedSession]; // 创建请求Task NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { // 将请求到的网页数据用loadHTMLString 的方法加载 NSString *htmlStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; [webView loadHTMLString:htmlStr baseURL:nil]; }]; // 开启网络任务 [task resume];
- 使用JavaScript解决WKWebView无法发送POST参数问题(iOS8)
-
iOS9以前版本读取本地HTML的问题
当使用loadRequest来读取本地的HTML时,WKWebView是无法读取成功的,后台会出现如下的提示:
Could not create a sandbox extension for /
原因是WKWebView是不允许通过loadRequest的方法来加载本地根目录的HTML文件。
而在iOS9的SDK中加入了以下方法来加载本地的HTML文件:
[WKWebView loadFileURL:allowingReadAccessToURL:]
但是在iOS9以下的版本是没提供这个便利的方法的。以下为解决方案的思路,就是在iOS9以下版本时,先将本地HTML文件的数据copy到tmp目录中,然后再使用loadRequest来加载。但是如果在HTML中加入了其他资源文件,例如js,css,image等必须一同copy到temp中。这个是最蛋疼的事情了。//将文件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; } //调用逻辑 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:在实际测试中,上述方法在iOS8.0系统中也无法成功。如果在iOS8.0系统有成功的请告知下。谢谢!