一提到webView監(jiān)聽或者攔截url,許多人可能都會想到在以下下兩個方法中攔截
- UIWebView
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType;
- WKWebView
// 頁面開始加載
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation;
或者
// 在發(fā)送請求之前,決定是否跳轉(zhuǎn)
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler;
這兩種情況可以處理一般情況下的問題,但是特殊情況下,比如手機百度首頁是多個頁面同時開始加載,然后再次點擊跳轉(zhuǎn)的時候就不會再調(diào)用以上的方法,或者點擊網(wǎng)頁返回上一頁的時候也不會調(diào)用以上的方法,這個時候我們可以采用下面的方式
- UIWebView
- 繼承NSURLCache類,并重寫- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request方法
.h
@interface XLURLCache: NSURLCache
@end
.m
@implementation XLURLCache
- (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request {
// 可以在此處進(jìn)行攔截并執(zhí)行相應(yīng)的操作
NSLog(@"url-------%@",request.URL.absoluteString);
if ([request.URL.absoluteString isEqualToString:@""]) {
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:@"text/plain" expectedContentLength:1 textEncodingName:nil];
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:[NSData dataWithBytes:" " length:1]];
[super storeCachedResponse:cachedResponse forRequest:request];
}
return [super cachedResponseForRequest:request];
}
@end
- 在AppDelegate中添加以下代碼
// 緩存路徑 可以是Documents或者tmp文件夾
NSString *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; // the path to the cache file
NSUInteger discCapacity = 10*1024*1024;
NSUInteger memoryCapacity = 512*1024;
XLURLCache *cache = [[XLURLCache alloc] initWithMemoryCapacity: memoryCapacity
diskCapacity: discCapacity diskPath:path];
[NSURLCache setSharedURLCache:cache];
- 然后,webView在加載新頁面的時候就會走1中的方法了,不過有個缺點就是網(wǎng)頁中圖片鏈接之類的也會調(diào)用1中的方法,調(diào)用次數(shù)會比較多
- WKWebView
WKWebView的方法比較簡單,我們只需要用KVO監(jiān)聽URL屬性的變化就能達(dá)到目的如下
[webView addObserver:self forKeyPath:@"URL" options:NSKeyValueObservingOptionNew context:nil];
// KVO 接收到通知時的方法
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
if ([keyPath isEqualToString:@"URL"]) {
// 可以在這里進(jìn)行攔截并做相應(yīng)的處理
NSLog(@"URL------%@",_webView.URL.absoluteString);
}
}
這里我們會發(fā)現(xiàn),那為什么UIWebView我們不去用KVO的監(jiān)聽來實現(xiàn)呢?其實,我在寫文章的時候也嘗試了一下UIWebView的KVO但發(fā)現(xiàn)無論是監(jiān)聽request屬性還是request.URL亦或request.URL.absoluteString都沒有調(diào)用KVO的監(jiān)聽回調(diào).
通過查看源碼我們可以發(fā)現(xiàn)UIWebView的request屬性.NSURLRequest的URL屬性,NSURL的absoluteString 以及WKWebView的URL屬性都是被關(guān)鍵字readonly修飾的,而KVO的原理是什么呢?
對于每個被觀察的 property(屬性),重寫其 setter 方法 。
在重寫的 setter 方法中調(diào)用以下方法通知觀察者 :
-willChangeValueForKey:
-didChangeValueForKey:
readonly導(dǎo)致了該屬性沒有setter方法,也就無法被KVO監(jiān)聽.但是為什么WKWebView的URL屬性能被監(jiān)聽到呢? 個人推測可能是一下兩個方案的一種
// 1.自己寫一個方法幫助系統(tǒng)補齊原本應(yīng)該在setter中實現(xiàn)的兩個觀察者方法 如下
- (void)ownSetURL:(NSURL *)URL {
[self willChangeValueForKey:@"URL"];
_URL = URL;
[self didChangeValueForKey:@"URL"];
}
// 2.
- (void)ownSetURL:(NSURL *)URL {
[self setValue:URL forKey:@"URL"];
}
至此,問題解決