WKWebView的使用说明

WKWebview的常见知识点介绍

wkWebView

WkWebViews是现在 WebKit API 在 iOS 8 和 OS X Yosemite 应用中的核心部分。它代替了 UIKit 中的 UIWebView 和 AppKit 中的 WebView,提供了统一的跨双平台 API。

自诩拥有 60fps 滚动刷新率、内置手势、高效的 app 和 web 信息交换通道、和 Safari 相同的 JavaScript 引擎,WKWebView 毫无疑问地成为了 WWDC 2014 上的最亮点

UIWebView & UIWebViewDelegate 这个两个东西是如何在 WKWebKit 中被重构成 14 个类 3 个协议的呢。虽然这次的变化确实带来了不少的新功能,但请一定不要因此感到恐慌!

WkWebview相关类之间的关系

wkWebview图片之间的关系

WKWebview相关类的说明

WKBackForwardList: 之前访问过的 web 页面的列表,可以通过后退和前进动作来访问到。
WKBackForwardListItem: webview 中后退列表里的某一个网页。
WKFrameInfo: 包含一个网页的布局信息。
WKNavigation: 包含一个网页的加载进度信息。
WKNavigationAction: 包含可能让网页导航变化的信息,用于判断是否做出导航变化。
WKNavigationResponse: 包含可能让网页导航变化的返回内容信息,用于判断是否做出导航变化。
WKPreferences: 概括一个 webview 的偏好设置。
WKProcessPool: 表示一个 web 内容加载池。
WKUserContentController: 提供使用 JavaScript post 信息和注射 script 的方法。
WKScriptMessage: 包含网页发出的信息。
WKUserScript: 表示可以被网页接受的用户脚本。WKWebViewConfiguration: 初始化 webview 的设置。
WKWindowFeatures: 指定加载新网页时的窗口属性。
WKNavigationDelegate: 提供了追踪主窗口网页加载过程和判断主窗口和子窗口是否进行页面加载新页面的相关方法。
WKScriptMessageHandler: 提供从网页中收消息的回调方法
WKUIDelegate: 提供用原生控件显示网页的方法回调。

WKNavigationDelegate

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
2. 当内容开始返回时调用
- (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation;
// 3. 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation;
// 页面加载失败时调用
(void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation;

// 接收到服务器跳转请求之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation;
// 4. 在收到响应后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
// 5. 在发送请求之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;

常用的调用方式以及常用方法

直接执行js语句

1
2
3
[[WKWebView alloc] evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id _Nullable result, NSError * _Nullable error) {

}];

基本注入 WKUserScript

What can User Scripts Do?

  • Modify the document
  • Listen for events
  • Load resources
  • Communicate back to your application.
  • WKUserScript *script = [[WKUserScript alloc] initWithSource:injectionJS injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];

注意:注入的事件只对于当前的WKWebView有效,并且如果js文件存在我们注入的同名函数,以我们注入的js代码效果为主

指定js方法进行回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
[configuration.userContentController addScriptMessageHandler:self name:@"jsCall"];
_wkWebView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
KWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc] init];
[configuration.userContentController addScriptMessageHandler:self name:@"jsCall"];
_wkWebView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];

js端调用的方式:
window.webkit.messageHandlers.jsCall.postMessage('test');
如果直接执行多次消息回调,不用担心回调的内容会丢失,也不用担心顺序会被改变掉
//代理回调
(void) userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{}
js端调用的方式:
window.webkit.messageHandlers.jsCall.postMessage('test');
如果直接执行多次消息回调,不用担心回调的内容会丢失,也不用担心顺序会被改变掉
//代理回调
(void) userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{}}

约定事件 后续再js中进行回传

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
js事件约定
var console = {
log:function(string) {
var dicParam = {paramsString:string};
var params = {className:'Console',classSelector:'log:',classParams:dicParam};
window.webkit.messageHandlers.jsCall.postMessage(params);
},
show:function(message) {
var dicParam = {'message':message};
var params = {className:'Console',classSelector:'show:',classParams:dicParam};
window.webkit.messageHandlers.jsCall.postMessage(params);
}
};

当我们在前端执行调用指定事件
console.log()或者console.show()事件,从而我们会在这里收到一个之前约定的消息体,从消息中解析出来,然后映射到指定的类中:


//解析符合约定规则的消息体
- (void) userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {

if([message.name isEqualToString:@"jsCall"]) {//这个约定事件会被
NSDictionary *classInfo = (NSDictionary *)message.body;
NSString *className = classInfo[@"className"];
NSString *classSelector = classInfo[@"classSelector"];
NSDictionary *classParams = classInfo[@"classParams"];//根据参数来区分执行的方法
if(className.length == 0 || classSelector.length == 0) {
return;
}
Class newClass = NSClassFromString(className);
NSObject *object = nil;
if(newClass) {
object = [[newClass alloc] init];
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
SEL selector = NSSelectorFromString(classSelector);
if([object respondsToSelector:selector]) {
if(classParams.count == 0) {
[object performSelector:selector withObject:nil];
} else if(classParams.count > 0) {
[object performSelector:selector withObject:classParams];
}
}
#pragma clang diagnostic pop
}
}

关于OC在这里映射的类和它对应的方法:
- (void)log:(NSDictionary *)info {
NSLog(@"test: %@ " , info);
}

- (void) show:(NSDictionary *) info {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"提示" message:info[@"message"] delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil];
[alertView show];
}

支持多文件的同时加载注入:

1
2
3
4
5
6
7
8
9
10
11
12

// 批量加载某个指定的js文件
NSString *console = @"console";
[self runPluginJS:@[console]];

//可以同时添加多个文件
for(NSString * jsFileName in array) {
NSString *path = [[NSBundle mainBundle] pathForResource:jsFileName ofType:@"js"];
NSString *fileContent = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
[self.wkWebView evaluateJavaScript:fileContent completionHandler:^(id response , NSError * error) {
}];
}

关于使用模拟器+safari进行

设置模拟器的开关,点击设置 -> safari -> 高级 -> Web检查器,设置开关打开

图片

启动模拟器后,直接打开safari,在菜单栏中点击开发 -> simulator-> 打开模拟器中对应的html文件

在出现的控制台中输入如下代码:

最终,你可以在- (void) userContentController:(WKUserContentController )userContentController didReceiveScriptMessage:(WKScriptMessage )message{}可以在这个方法中接收我们传入的参数信息,完美可以使用safari进行注册事件测试。