WKWebView用法

1、配置WKWebView

1
2
3
4
5
6
7
8
9
10
11
// 初始化config
WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
config.userContentController = [[WKUserContentController alloc] init];
config.preferences = [[WKPreferences alloc] init];

// 初始化webView
_webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];

// 设置代理
_webView.UIDelegate = self;
_webView.navigationDelegate = self;

2、加载本地html文件

1
2
3
4
NSString *path = [[NSBundle mainBundle] pathForResource:@"XXX.html" ofType:nil];
NSString *htmlString = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];

[_webView loadHTMLString:htmlString baseURL:[NSBundle.mainBundle bundleURL]];

3、加载url

1
2
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL fileURLWithPath:path] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30]; 
[_webView loadRequest:request];

4、适配文本大小

1
2
3
4
NSString *jSString = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);";
WKUserScript *wkUScript = [[WKUserScript alloc] initWithSource:jSString injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];

[config.userContentController addUserScript:wkUScript];

5、js调用OC方法(无参数)

1
2
3
4
5
6
7
8
9
10
11
12
	// 注册js回调
[config.userContentController addScriptMessageHandler:self name:@"jsToOCNoParam"];

// 实现 WKScriptMessageHandler 代理的方法:
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSLog(@"%@",message.name);
NSString *name = message.name;
if ([name isEqualToString:@"jsToOCNoParam"]) {
// 处理业务
// do something
}
}
1
2
3
4
5
6
7
// js代码
<button id="btn1" type = "button" onclick = "jsToOCNoParam()" > JS调用OC:不带参数 </button>
...
function jsToOCNoParam() {
window.webkit.messageHandlers.jsToOCNoParam.postMessage({});
}
// 注:messageHandlers后面的方法名要和config注册的保持一致

6、js调用OC方法(带参数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
	// 注册js回调
[config.userContentController addScriptMessageHandler:self name:@"jsToOCWithParams"];

// 实现 WKScriptMessageHandler 代理的方法:
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSLog(@"%@",message.name);
NSString *name = message.name;
if ([name isEqualToString:@"jsToOCWithParams"]) {
// 处理业务
// 获取参数
NSDictionary *params = message.body;
// do something
}
}
1
2
3
4
5
6
// js代码
<button id="btn1" type = "button" onclick = "jsToOCWithParams()" > JS调用OC:不带参数 </button>
...
function jsToOCWithParams() {
window.webkit.messageHandlers.jsToOCWithParams.postMessage({"param1": "参数1", "param2":"参数2"});
}

7、监听网页加载进度和标题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
	// 利用KVO实现	
// 监听网页加载进度
[_webView addObserver:self forKeyPath:@"estimatedProgress" options:0 context:nil];
// 监听title
[_webView addObserver:self forKeyPath:@"title" options:NSKeyValueObservingOptionNew context:nil];

...
// 实现KVO
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"estimatedProgress"] && object == _webView) {
// 获取进度
NSLog(@"网页加载的进度:%f", _webView.estimatedProgress);
// do something
} else if ([keyPath isEqualToString:@"title"] && object == _webView) {
// 获取title
NSLog(@"title:%@",_webView.title);
// do something
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}

8、WKNavigationDelegate代理相关

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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// 页面开始加载时调用
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
// do something
}

// 页面加载失败时调用
- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
// do something
}

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

// 页面加载完成之后调用
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
// do something
}

//提交发生错误时调用
- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error {
// do something
}

// 接收到服务器跳转请求即服务重定向时之后调用
- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
// do something
}

// 根据WebView对于即将跳转的HTTP请求头信息和相关信息来决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSString * urlStr = navigationAction.request.URL.absoluteString;
NSLog(@"发送跳转请求:%@",urlStr);
//自己定义的协议头
NSString *htmlHeadString = @"xxx://";
if([urlStr hasPrefix:htmlHeadString]){
// do something
...
// 取消跳转
decisionHandler(WKNavigationActionPolicyCancel);
}else{
// 允许跳转
decisionHandler(WKNavigationActionPolicyAllow);
}
}

// 根据客户端受到的服务器响应头以及response相关信息来决定是否可以跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
NSString * urlStr = navigationResponse.response.URL.absoluteString;
NSLog(@"当前跳转地址:%@",urlStr);
//允许跳转
decisionHandler(WKNavigationResponsePolicyAllow);
//不允许跳转
//decisionHandler(WKNavigationResponsePolicyCancel);
}

//需要响应身份验证时调用 同样在block中需要传入用户身份凭证
- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler{
//用户身份信息
NSURLCredential * newCred = [[NSURLCredential alloc] initWithUser:@"username" password:@"password" persistence:NSURLCredentialPersistenceNone];
//为 challenge 的发送方提供 credential
[challenge.sender useCredential:newCred forAuthenticationChallenge:challenge];
completionHandler(NSURLSessionAuthChallengeUseCredential,newCred);
}

//进程被终止时调用
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView{
// do something
}

9、WKUIDelegate代理相关

  • 提示框
1
2
3
4
5
6
7
8
9
10
// 提示框
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
// 显示弹框内容
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"HTML的弹出框" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
// 一定要调用这个block。否则会崩溃
completionHandler();
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
1
2
3
function alertToOC() {
alert("弹出一个提示框");
}
  • 确认框
1
2
3
4
5
6
7
8
9
10
11
12
13
// 确认框
// JavaScript调用confirm方法后回调的方法 confirm是js中的确定框,需要在block中把用户选择的情况传递进去
- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
// 显示确认框
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"HTML的确认框" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
completionHandler(NO);
}])];
[alertController addAction:([UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(YES);
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
1
2
3
4
function confirmToOC() {
let b = confirm("弹出一个确认框");
// b 为bool类型数据
}
  • 输入框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 输入框
// JavaScript调用prompt方法后回调的方法 prompt是js中的输入框 需要在block中把用户输入的信息传入
- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
// 显示输入弹框
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert];
[alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.text = defaultText;
textField.placeholder = prompt;
}];
[alertController addAction:([UIAlertAction actionWithTitle:@"完成" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
completionHandler(alertController.textFields[0].text?:@"");
}])];
[self presentViewController:alertController animated:YES completion:nil];
}
1
2
3
4
5
6
7
8
function showPrompt() {
let text = prompt("请输入信息", "这个默认值");
// text 为输入值。

// do something like this
var btn = document.getElementById('btn4');
btn.innerHTML = text;
}
  • 新窗口处理
1
2
3
4
5
6
7
// 需要从新窗口中加载网页时(target = _blank)
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
if (!navigationAction.targetFrame.isMainFrame) {
[webView loadRequest:navigationAction.request];
}
return nil;
}
1
<p> <a href="https://www.baidu.com" target="_blank" >打开新窗口</a> </p>