WKWebView的使用

在WWDC 2014中,苹果推出了最新的 iOS8 系统,其中也伴随着很多控件的更新与升级。其中全新的WebKit库让人很是兴奋。本文也将讲解到WebKit中更新的WKWebView控件的新特性与使用方法,它很好的解决了UIWebView存在的内存、加载速度等诸多问题。

WKWebView新特性

在性能、稳定性、功能方面有很大提升(最直观的体现就是加载网页是占用的内存,模拟器加载百度与开源中国网站时,WKWebView占用23M,而UIWebView占用85M)。

允许JavaScript的Nitro库加载并使用(UIWebView中限制)。

支持了更多的HTML5特性。

高达60fps的滚动刷新率以及内置手势。

将UIWebViewDelegate与UIWebView重构成了14类与3个协议。

初始化

1.首先需要引入WebKit库:

1
#import <WebKit/WebKit.h>

2.初始化方法分为以下两种:

1
2
3
4
5
// 默认初始化
- (instancetype)initWithFrame:(CGRect)frame;

// 根据对webview的相关配置,进行初始化
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration NS_DESIGNATED_INITIALIZER;

3.加载网页与HTML代码的方式与UIWebView相同:

1
2
3
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]]];
[self.view addSubview:webView];

WKWebView的代理方法

1.WKNavigationDelegate

该代理提供的方法,可以用来追踪加载过程(页面开始加载、加载完成、加载失败)、决定是否执行跳转。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
// 类似UIWebView的 -webViewDidStartLoad:
NSLog(@"didStartProvisionalNavigation");
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
}

// 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;

// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
// 类似 UIWebView 的 -webViewDidFinishLoad:
NSLog(@"didFinishNavigation");
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}

// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;
页面跳转的代理方法有三种,分为(收到跳转与决定是否跳转两种)
1
2
3
4
5
6
7
8
// 接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
-
// 在收到响应后,决定是否跳转,类似 UIWebView - webView: shouldStartLoadWithRequest: navigationType:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;

// 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:

2.WKUIDelegate

1
2
3
4
5
6
// 创建一个新的WebView
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures{
// 接口的作用是打开新窗口委托
[self createNewWebViewWithURL:webView.URL.absoluteString config:configuration];
return currentSubView.webView;
}

剩下三个代理方法全都是与界面弹出提示框相关的,针对于web界面的三种提示框(警告框、确认框、输入框)分别对应三种代理方法。下面只举了警告框的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* web界面中有弹出警告框时调用
*
* @param webView 实现该代理的webview
* @param message 警告框中的内容
* @param frame 主窗口
* @param completionHandler 警告框消失调用
*/

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void (^)())completionHandler{
// js 里面的alert实现,如果不实现,网页的alert函数无效
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message
message:nil
preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:[UIAlertAction actionWithTitle:@"确定"
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action) {
completionHandler();
}]];

[self presentViewController:alertController animated:YES completion:^{}];
};

- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler {
completionHandler(@"Client Not handler");
}

3.WKScriptMessageHandler

这个协议中包含一个必须实现的方法,这个方法是提高App与web端交互的关键,它可以直接将接收到的JS脚本转为OC或Swift对象。(当然,在UIWebView也可以通过“曲线救国”的方式与web进行交互,著名的Cordova框架就是这种机制)

1
2
// 从web界面中接收到一个脚本时调用
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;

WKWebView加载JS

1
2
3
4
5
6
7
8
9
10
11
12
// 图片缩放的js代码
NSString *js = @"var count = document.images.length;for (var i = 0; i < count; i++) {var image = document.images[i];image.style.width=320;};window.alert('找到' + count + '张图');";

// 根据JS字符串初始化WKUserScript对象
WKUserScript *script = [[WKUserScript alloc] initWithSource:js injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];

// 根据生成的WKUserScript对象,初始化WKWebViewConfiguration
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
[config.userContentController addUserScript:script];
_webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
[_webView loadHTMLString:@"<head></head><img src='http://www.nsu.edu.cn/v/2014v3/img/background/3.jpg' />"baseURL:nil];
[self.view addSubview:_webView];

WKWebView 执行脚本方法

1
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler;

completionHandler 拥有两个参数,一个是返回错误,一个可以返回执行脚本后的返回值。

WKWebView 的Cookie问题

UIWebView 中会自动保存Cookie,如果登录了一次,下次再次进入的时候,会记住登录状态。

而在WKWebView中,并不会这样,WKWebView在初始化的时候有一个方法:

1
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration

通过这个方法,设置 configuration 让WKWebView知道登录状态,configuration 可以通过已有的Cookie进行设置,也可以通过保存上一次的configuration进行设置。

1
2
3
4
5
WKWebView * webView = /*set up your webView*/  
NSMutableURLRequest * request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://example.com/index.html"]];
[request addValue:@"TeskCookieKey1=TeskCookieValue1;TeskCookieKey2=TeskCookieValue2;" forHTTPHeaderField:@"Cookie"];
// use stringWithFormat: in the above line to inject your values programmatically
[webView loadRequest:request];
1
2
3
4
5
6
7
8
9
WKUserContentController* userContentController = WKUserContentController.new;  
WKUserScript * cookieScript = [[WKUserScript alloc]
initWithSource: @"document.cookie = 'TeskCookieKey1=TeskCookieValue1';document.cookie = 'TeskCookieKey2=TeskCookieValue2';"
injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
// again, use stringWithFormat: in the above line to inject your values programmatically
[userContentController addUserScript:cookieScript];
WKWebViewConfiguration* webViewConfig = WKWebViewConfiguration.new;
webViewConfig.userContentController = userContentController;
WKWebView * webView = [[WKWebView alloc] initWithFrame:CGRectMake(/*set your values*/) configuration:webViewConfig];

关于JS的加载或WebView的其他使用技巧,可查看一下文章:

使用safari对webview进行调试

WebView加载HTML图片大小自适应与文章自动换行


以上