鉴于UIWebView的内存问题,新项目选择了WKWebView来加载URL详情页面,本文将围绕“加载URL详情页面,并确保内部链接正常跳转”的需求,细聊开发过程中遇到的问题。(注:本文不涉及js交互、缓存相关的内容)
一、WKWebView的基本使用
1、加载指定的URL
- (void)gotoWebViewWithUrl:(NSString *)Url
{
WKWebView *webview = [[WKWebView alloc]initWithFrame:self.view.bounds];
[webview loadRequest:[NSURL URLWithString: Url];
}
2、代理
(1) 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;
(2) WKUIDelegate
JS交互时会用到这个代理,本文讨论的需求不涉及JS交互,所以这里不在赘述。
二、问题及解决方案
1、黑屏闪现
问题描述: 执行[webview loadRequest:]加载URL页面时,偶尔会有黑屏闪现的现象。
解决方案: 在初始化webView时,加载一个空白页面。
// 加载空白页
- (void)loadBlankHtml
{
static NSString *blankHtmlStr = @"<!DOCTYPE html>\
<html lang=\"en\">\
<head>\
<meta charset=\"UTF-8\">\
<title>Document</title>\
<style>body{background-color: #111111;}</style>\
</head>\
<body></body>\
</html>";
[self.webView loadHTMLString:blankHtmlStr baseURL:[NSURL URLWithString:kAboutBlank]];
}
2、加载进度、加载超时问题
问题描述:
点击webView的某个链接跳转至新的页面时,会异步的触发N个请求(多次触发回调1)来获取页面的多个模块数据,其中某些请求可能不会响应(不会触发回调2),某些请求可能不会有成功、失败的回调。
所以,无法通过监听请求的开始、结束获取加载进度,判断加载超时。
// 回调1: 在请求开始加载之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
// 回调2: 在收到响应开始加载后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;
解决方案:请求触发回调2方法后才算真正的开始,展示进度条、启动超时检测,请求有成功、失败的返回时,隐藏进度条、停止超时检测。
// 在收到响应开始加载后,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
{
if (NO == self.progressView.hidden) {
[self restartOutTimer];
self.progressView.hidden = hide;
}
if (decisionHandler) {
decisionHandler(WKNavigationResponsePolicyAllow);
}
}
3、加载超时处理
问题描述:
加载超时或没有网络时,需要在页面中告知用户,同时不影响现有的webView页面层级关系。(例如,加载页面后断网,点击页面内链接,需要告知用户没有网络,用户可以通过返回按钮返回到刚才的页面)
解决方案: 加载本地html页面。
// 在请求开始加载之前,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
NSString* requestUrl = [navigationAction.request.URL absoluteString];
// 没有网络时,展示本地url页面
if (self.curNetworkStatus == NetworkNotReachable &&
navigationAction.navigationType != WKNavigationTypeBackForward) {
NSString *path = [[NSBundle mainBundle] pathForResource:@"NoNetwork" ofType:@"html"];
if (![requestUrl isEqualToString:self.rootUrl]) {
[self loadFileURLWithPath:path];
} else {
[self loadHTMLStringWithPath:path];
}
decisionHandler(WKNavigationActionPolicyCancel);
}
}
3、跳转App Store问题
WKWebView不支持https://itunes.apple.com/cn/app/id******这样的链接跳转App Store,需要做出如下的处理才能正常跳转。
// 在请求开始加载之前调用,决定是否跳转
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
// itunes跳转链接需要单独处理
if ([[navigationAction.request.URL host] isEqualToString:@"itunes.apple.com"] &&
[[UIApplication sharedApplication] openURL:navigationAction.request.URL]) {
decisionHandler(WKNavigationActionPolicyCancel);
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}