一.前言

上一篇文章已经对WKWebView做了一个简单的介绍,主要对它的一些方法和属性做了一个简单的介绍,今天看一下WKWebView的两个协议:WKNavigationDelegate 和 WKUIDelegate。

二.WKNavigationDelegate

根据字面意思,它的作用是用于导航(navigation)的代理。其实里面定义了n多个方法,用于处理网页接受、加载和导航请求等自定义的行为。直接拿下面的例子来看:

#pragma mark - WKWebView NavigationDelegate

//WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSLog(@"是否允许这个导航");
    decisionHandler(WKNavigationActionPolicyAllow);
}

- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
//    Decides whether to allow or cancel a navigation after its response is known.

    NSLog(@"知道返回内容之后,是否允许加载,允许加载");
    decisionHandler(WKNavigationResponsePolicyAllow);
}
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"开始加载");
    self.progress.alpha  = 1;
    
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    
}

- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"跳转到其他的服务器");
    
}

- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
    NSLog(@"网页由于某些原因加载失败");
    self.progress.alpha  = 0;
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"网页开始接收网页内容");
}
- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation {
    NSLog(@"网页导航加载完毕");
    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    
    self.title = webView.title;
    [webView evaluateJavaScript:@"document.title" completionHandler:^(id _Nullable ss, NSError * _Nullable error) {
        NSLog(@"----document.title:%@---webView title:%@",ss,webView.title);
    }];
    self.progress.alpha  = 0;
    
}

- (void)webView:(WKWebView *)webView didFailNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error {
    NSLog(@"加载失败,失败原因:%@",[error description]);
    self.progress.alpha = 0;
}
- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
    NSLog(@"网页加载内容进程终止");
}
//- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
//    NSLog(@"receive");
//}

首先看一下(WKN1):

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
    NSLog(@"是否允许这个导航");
    decisionHandler(WKNavigationActionPolicyAllow);
}

这个方法是加载网页第一个执行的方法,因为它要确定是否允许或者取消加载这个导航。这里有一个枚举WKNavigationActionPolicy,其结构如下:

typedef NS_ENUM(NSInteger, WKNavigationActionPolicy) {
    WKNavigationActionPolicyCancel,
    WKNavigationActionPolicyAllow,
} API_AVAILABLE(macosx(10.10), ios(8.0));

这里的两个枚举确定了这个网页是否加载,我们只需要在decisionHandler回调里面传入相应的枚举值即可。这里可以用来处理自己不允许加载的网页,比如你的app里面的网页很多,但是domain只有两个,如果你只想加载这两个domain里面的网页,其他的domain不加载,那么可以在这里进行处理。(可以用来屏蔽移动或联通运营商推送的网页,让其不在app中展示

这里还有一个WKNavigationAction。它是一个导航动作, 包含了点击之后的导航动作,我们做过滤的时候可以通过该动作决定是否允许加载。它有两个关键的FrameInfo:

sourceFrame
targetFrame

他们都是WKFrameInfo的实例。该类包含了网页加载的frame的信息。该类有一个重要的属性:mainFrame。它是一个Bool值,用于标识该frame是不是当前网页的主frame或者是子frame。举个例子:

当我们第一次打开百度的时候,navigationAction是这样的:

<WKNavigationAction: 0x100410fa0; navigationType = -1; syntheticClickType = 0;
request = <NSMutableURLRequest: 0x170019a10> { URL: https://www.baidu.com/ };
sourceFrame = (null); targetFrame = <WKFrameInfo: 0x100401200; isMainFrame = YES; request = (null)>>

它的request url是https://www.baidu.com/。它的sourceFrame是nil,也就是请求导航的frame是空的。它的targetFrame是:

<WKFrameInfo: 0x100401200; isMainFrame = YES; request = (null)>

这里是在当前的frame打开,也就是目的的frame。当我点击新闻那个链接的时候,其navigationAction是这样的:

<WKNavigationAction: 0x1004120b0; navigationType = -1; syntheticClickType = 0; request = <NSMutableURLRequest: 0x17001af30> 
{ URL: http://m.news.baidu.com/news?fr=mohome&ssid=0&from=844b&uid=&pu=sz%401320_2001%2Cta%40iphone_1_10.2_3_602&bd_page_type=1
}; sourceFrame = (null); targetFrame = <WKFrameInfo: 0x100414ff0; isMainFrame = YES; request = <NSMutableURLRequest: 0x17001b080>
{ URL:
https://www.baidu.com/ }>>

可以看到,此时的导航动作是要请求的

http://m.news.baidu.com/news?fr=mohome&ssid=0&from=844b&uid=&pu=sz%401320_2001%2Cta%40iphone_1_10.2_3_602&bd_page_type=1

也就是网页版百度新闻的链接。此时它的sourceFrame是nil,它的targetFrame是:

<WKFrameInfo: 0x100414ff0; isMainFrame = YES; request = <NSMutableURLRequest: 0x17001b080> { URL: https://www.baidu.com/ }>

它的目标frame的request是baidu。其中的isMainFrame属性是YES,说明是主的frame,所以还是在当前的网页中打开一个新链接。

此时我们也许会遇到另外一种情况,就是sourceFrame不为空,但是targetFrame为空,这里如果targetFrame为空,那么这个就是新建一个window 导航。用我们自己的话来说就是重新打开了一个tab页面。

WKWebView代理方法解析

就类似这样,我点击了PC版的网页链接,然后新开了一个tab,这样的话sourceFrame就是当前的百度,而targetFrame就是空的。此时(我用这个网页测试)你会发现这个代理方法不执行了,好尴尬。。。。处理都不知道怎么处理了。由于新打开了tab,也就意味着我们的请求不在当前的网页加载了,那么也无法调用到这个代理方法了。因此我们需要重新配置这个新打开的网页,这里就会调用:

- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration 
forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures;

这个代理方法是WKUIDelegate的代理方法,可在下面查看。

接着就是开始加载(WKN2):

- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(null_unspecified WKNavigation *)navigation;

这个方法比较好理解,就是当网页内容开始加载到web view的时候调用,这里的navigation没有其他特殊含义,看一下WKNavigation这个类可以知道,他就是NSObject的一个子类,而且里面没有任何新增的其他方法或者属性。我想仅仅是为了名字上能够好理解才这样写的吧。

再接着就是(WKN3):

- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse 
decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler;

这个代理方法了。我们知道了网页是否允许加载,那么一旦不允许,那么这个加载过程就已经结束了,不会再执行其他的代理方法;如果允许,那么就会执行开始加载的代理方法,执行完开始加载的代理方法的时候再执行这个代理方法。

根据意思可知,它的作用就是要根据导航的返回信息来判断是否加载网页。我们首先打印出navigationResponse的信息:

<WKNavigationResponse: 0x100323e50; response = <NSHTTPURLResponse: 0x170226780> { URL: https://www.baidu.com/ } { status code: 200, headers {
    "Cache-Control" = "no-cache";
    Connection = "keep-alive";
    "Content-Encoding" = gzip;
    "Content-Length" = 20059;
    "Content-Type" = "text/html;charset=utf-8";
    Date = "Mon, 27 Mar 2017 05:29:56 GMT";
    Server = "bfe/1.0.8.18";
    "Set-Cookie" = "H_WISE_SIDS=108266_100186_114821_114654_114743_109815_103550_114996_114701_112106_107314_114132_115245_115109_115056_115244_115043_114797_114513_114998_115227_114329_114534_115032_114276_114975_110085; path=/; domain=.baidu.com, BDSVRTM=182; path=/, __bsi=11762462753482193024_00_281_N_N_189_0303_C02F_N_N_Y_0; expires=Mon, 27-Mar-17 05:30:01 GMT; domain=www.baidu.com; path=/";
    "Strict-Transport-Security" = "max-age=172800";
    Traceid = 149059259607016350822661639119137312881;
} }>

这个response有个属性叫做forMainFrame,用于标识导航的frame是不是主frame。

navigationResponse的canShowMIMEType属性用于表示WebKit是否能够展示返回的MIME类型。

如果我们拿到了返回的信息,发现这些信息我们不需要,我们可以在此方法里面进行处理,然后决定是否加载该网页

接下来执行的是(WKN4):

- (void)webView:(WKWebView *)webView didCommitNavigation:(null_unspecified WKNavigation *)navigation;

这个代理方法是在网页开始接受网络内容的时候调用,也就是网络内容开始要往网页中加载。

再接着就是(WKN5):

- (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation;

意思就是这个导航我们已经加载完成了。我的理解就是这个当前网页加载完毕。

 还有剩下的三个方法,一个加载网页失败的方法,一个接收服务器跳转方法和一个网页加载进程终止的方法。

网页加载失败方法:

- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(null_unspecified WKNavigation *)navigation withError:(NSError *)error;

当网页由于error加载失败就会调用这个代理方法,它会将具体的error信息给抛出,然后供开发者具体情况具体处理。这里的error code具体在NSURLError.h里面定义,具体为:

NS_ENUM(NSInteger)
{
    NSURLErrorUnknown =             -1,
    NSURLErrorCancelled =             -999,
    NSURLErrorBadURL =                 -1000,
    NSURLErrorTimedOut =             -1001,
    NSURLErrorUnsupportedURL =             -1002,
    NSURLErrorCannotFindHost =             -1003,
    NSURLErrorCannotConnectToHost =         -1004,
    NSURLErrorNetworkConnectionLost =         -1005,
    NSURLErrorDNSLookupFailed =         -1006,
    NSURLErrorHTTPTooManyRedirects =         -1007,
    NSURLErrorResourceUnavailable =         -1008,
    NSURLErrorNotConnectedToInternet =         -1009,
    NSURLErrorRedirectToNonExistentLocation =     -1010,
    NSURLErrorBadServerResponse =         -1011,
    NSURLErrorUserCancelledAuthentication =     -1012,
    NSURLErrorUserAuthenticationRequired =     -1013,
    NSURLErrorZeroByteResource =         -1014,
    NSURLErrorCannotDecodeRawData =             -1015,
    NSURLErrorCannotDecodeContentData =         -1016,
    NSURLErrorCannotParseResponse =             -1017,
    NSURLErrorAppTransportSecurityRequiresSecureConnection NS_ENUM_AVAILABLE(10_11, 9_0) = -1022,
    NSURLErrorFileDoesNotExist =         -1100,
    NSURLErrorFileIsDirectory =         -1101,
    NSURLErrorNoPermissionsToReadFile =     -1102,
    NSURLErrorDataLengthExceedsMaximum NS_ENUM_AVAILABLE(10_5, 2_0) =    -1103,
    
    // SSL errors
    NSURLErrorSecureConnectionFailed =         -1200,
    NSURLErrorServerCertificateHasBadDate =     -1201,
    NSURLErrorServerCertificateUntrusted =     -1202,
    NSURLErrorServerCertificateHasUnknownRoot = -1203,
    NSURLErrorServerCertificateNotYetValid =     -1204,
    NSURLErrorClientCertificateRejected =     -1205,
    NSURLErrorClientCertificateRequired =    -1206,
    NSURLErrorCannotLoadFromNetwork =         -2000,
    
    // Download and file I/O errors
    NSURLErrorCannotCreateFile =         -3000,
    NSURLErrorCannotOpenFile =             -3001,
    NSURLErrorCannotCloseFile =         -3002,
    NSURLErrorCannotWriteToFile =         -3003,
    NSURLErrorCannotRemoveFile =         -3004,
    NSURLErrorCannotMoveFile =             -3005,
    NSURLErrorDownloadDecodingFailedMidStream = -3006,
    NSURLErrorDownloadDecodingFailedToComplete =-3007,

    NSURLErrorInternationalRoamingOff NS_ENUM_AVAILABLE(10_7, 3_0) =         -1018,
    NSURLErrorCallIsActive NS_ENUM_AVAILABLE(10_7, 3_0) =                    -1019,
    NSURLErrorDataNotAllowed NS_ENUM_AVAILABLE(10_7, 3_0) =                  -1020,
    NSURLErrorRequestBodyStreamExhausted NS_ENUM_AVAILABLE(10_7, 3_0) =      -1021,
    
    NSURLErrorBackgroundSessionRequiresSharedContainer NS_ENUM_AVAILABLE(10_10, 8_0) = -995,
    NSURLErrorBackgroundSessionInUseByAnotherProcess NS_ENUM_AVAILABLE(10_10, 8_0) = -996,
    NSURLErrorBackgroundSessionWasDisconnected NS_ENUM_AVAILABLE(10_10, 8_0)= -997,
};
View Code

相关文章:

  • 2021-11-20
  • 2022-12-23
  • 2022-01-02
  • 2022-12-23
  • 2021-06-23
  • 2021-04-03
  • 2022-12-23
  • 2021-06-12
猜你喜欢
  • 2021-07-30
  • 2021-05-17
  • 2022-12-23
  • 2021-10-09
  • 2022-12-23
  • 2022-12-23
  • 2021-08-14
相关资源
相似解决方案