ios8以后苹果推出了webkit框架,替换UIWebView。
特性:
1. 在性能、稳定性、功能方面有很大提升(最直观的体现就是加载网页是占用的内存,模拟器加载百度与开源中国网站时,WKWebView占用23M,而UIWebView占用85M);
2. 允许JavaScript的Nitro库加载并使用(UIWebView中限制);
3. 支持了更多的HTML5特性;
4. 高达60fps的滚动刷新率以及内置手势;
5. 将UIWebViewDelegate与UIWebView重构成了14类与3个协议
UIWebView:加载速度慢、占用内存多、优化困难、如果加载网页多的话会因为占用大量内存而使系统被kill掉。
一、WKWebView Framework
类:
- WKBackForwardList: 之前访问过的 web 页面的列表,可以通过后退和前进动作来访问到。
- WKBackForwardListItem: webview 中后退列表里的某一个网页。
- WKFrameInfo: 包含一个网页的布局信息。
- WKNavigation: 包含一个网页的加载进度信息。
- WKNavigationAction: 包含可能让网页导航变化的信息,用于判断是否做出导航变化。
- WKNavigationResponse: 包含可能让网页导航变化的返回内容信息,用于判断是否做出导航变化。
- WKPreferences: 概括一个 webview 的偏好设置。
- WKProcessPool: 表示一个 web 内容加载池。
- WKUserContentController: 提供使用 JavaScript post 信息和注射 script 的方法。
- WKScriptMessage: 包含网页发出的信息。
- WKUserScript: 表示可以被网页接受的用户脚本。
- WKWebViewConfiguration: 初始化 webview 的设置。
- WKWindowFeatures: 指定加载新网页时的窗口属性。
- WKWebsiteDataStore: 包含网页数据存储和查找。
协议:
- WKNavigationDelegate: 提供了追踪主窗口网页加载过程和判断主窗口和子窗口是否进行页面加载新页面的相关方法。
- WKScriptMessageHandler: 这个协议中包含一个必须实现的方法,这个方法是native与web端交互的关键,它可以直接将接收到的JS脚本转为OC或Swift对象。
- WKUIDelegate: 提供用原生控件显示网页的方法回调。
二、WKWebView中的三个代理方法
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;
WKUIDelegate
// 创建一个新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;
剩下三个代理方法全都是与界面弹出提示框相关的,针对于web界面的三种提示框(警告框、确认框、输入框)分别对应三种代理方法。
// 界面弹出警告框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void (^)())completionHandler;
// 界面弹出确认框
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler;
// 界面弹出输入框
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler;
WKScriptMessageHandler
这个协议中包含一个必须实现的方法,这个方法是native与web端交互的关键,它可以直接将接收到的JS脚本转为OC或Swift对象。
// 从web界面中接收到一个脚本时调用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
案例Demo
注:WKWebView不支持nib文件,所以这里需要使用代码初始化并加载WebView
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.preferences.minimumFontSize = 18;
self.wkWebView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height/2) configuration:config];
[self.view addSubview:self.wkWebView];
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html"];
NSURL *baseURL = [[NSBundle mainBundle] bundleURL];
[self.wkWebView loadHTMLString:[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil] baseURL:baseURL];
WKUserContentController *userCC = config.userContentController;
OC端
//JS调用OC 添加处理脚本
[userCC addScriptMessageHandler:self name:@"showMobile"];
[userCC addScriptMessageHandler:self name:@"showName"];
[userCC addScriptMessageHandler:self name:@"showSendMsg"];
#pragma mark - WKScriptMessageHandler
代理方法中处理事件
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSLog(@"%@",NSStringFromSelector(_cmd));
NSLog(@"%@",message.body);
if ([message.name isEqualToString:@"showMobile"]) {
[self showMsg:@"我是下面的小红 手机号是:18870707070"];
}
if ([message.name isEqualToString:@"showName"]) {
NSString *info = [NSString stringWithFormat:@"你好 %@, 很高兴见到你",message.body];
[self showMsg:info];
}
if ([message.name isEqualToString:@"showSendMsg"]) {
NSArray *array = message.body;
NSString *info = [NSString stringWithFormat:@"这是我的手机号: %@, %@ !!",array.firstObject,array.lastObject];
[self showMsg:info];
}
}
native调用js
//网页加载完成之后调用JS代码才会执行,因为这个时候html页面已经注入到webView中并且可以响应到对应方法
- (IBAction)btnClick:(UIButton *)sender {
if (!self.wkWebView.loading) {
if (sender.tag == 123) {
[self.wkWebView evaluateJavaScript:@"alertMobile()" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
//TODO
NSLog(@"%@ %@",response,error);
}];
}
if (sender.tag == 234) {
[self.wkWebView evaluateJavaScript:@"alertName('小红')" completionHandler:nil];
}
if (sender.tag == 345) {
[self.wkWebView evaluateJavaScript:@"alertSendMsg('18870707070','周末爬山真是件愉快的事情')" completionHandler:nil];
}
} else {
NSLog(@"the view is currently loading content");
}
}
JS端:
function clear() {
document.getElementById('mobile').innerHTML = ''
document.getElementById('name').innerHTML = ''
document.getElementById('msg').innerHTML = ''
}
//OC调用JS的方法列表
function alertMobile() {
//这里已经调用过来了 但是搞不明白为什么alert方法没有响应
//alert('我是上面的小黄 手机号是:13300001111')
document.getElementById('mobile').innerHTML = '我是上面的小黄 手机号是:13300001111'
}
function alertName(msg) {
//alert('你好 ' + msg + ', 我也很高兴见到你')
document.getElementById('name').innerHTML = '你好 ' + msg + ', 我也很高兴见到你'
}
function alertSendMsg(num,msg) {
//window.alert('这是我的手机号:' + num + ',' + msg + '!!')
document.getElementById('msg').innerHTML = '这是我的手机号:' + num + ',' + msg + '!!'
}
//JS响应方法列表
function btnClick1() {
window.webkit.messageHandlers.showMobile.postMessage(null)
}
function btnClick2() {
window.webkit.messageHandlers.showName.postMessage('xiao黄')
}
function btnClick3() {
window.webkit.messageHandlers.showSendMsg.postMessage(['13300001111', 'Go Climbing This Weekend !!!'])
}
三、使用注意点
WKWebView只能用代码创建,而且自身就支持了右滑返回手势allowsBackForwardNavigationGestures和加载进度estimatedProgress等一些UIWebView不具备却非常好用的属性。在创建的时候,指定初始化方法中要求传入一个WKWebViewConfiguration对象,一般我们使用默认配置就好,但是有些地方是要根据自己的情况去做更改。比如,配置中的allowsInlineMediaPlayback这个属性,默认为NO,如果不做更改,网页中内嵌的视频就无法正常播放。
更改User-Agent
在iOS9,WKWebView提供了一个非常便捷的属性去更改User-Agent,就是customUserAgent属性。这样使用起来不仅方便,也不会全局更改User-Agent。
欢迎大家加入iOS开发交流群,群号:474547529。