WKWebView或UIWebView无法拦截的页面地址变更问题

简介: WKWebView或UIWebView无法拦截的页面地址变更问题

大家有没有发现微信端的js代码移植到ios app上,当点击js上的按钮跳转到页面,咱们的WKWebView或UIWebView无法拦截到这个地址的变更。

当h5页面的第一页面为下面的地址:http://test/market/homepage.htm,当点击他们上的按钮跳转到一个新的网页地址(https://test/market/homepage.htm#!/https://test/market/auction/detail.htm?aId=12)并刷新页面,而UIWebView的页面加载完回调函数(-(void)webViewDidFinishLoad:(UIWebView )webView)和加载前函数(-(BOOL)webView:(UIWebView )webView shouldStartLoadWithRequest:(nonnull NSURLRequest )request navigationType:(UIWebViewNavigationType)navigationType)没有任何回调;同样WKWebView的页面加载完回调函数(-(void)webView:(WKWebView )webView didFinishNavigation:(WKNavigation )navigation)和服务器开始请求的时候调用回调函数(- (void)webView:(WKWebView )webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler)没有任何回调。我把这两个控件的所有协议函数都实现也没有看到它回调他们,并且连WKWebView的进度条 KVO监听函数也没有回调,导致页面跳转时没有显示进度条。

我研究了两天发现,无语了,WKWebView根本根本无法监控到这种页面的变化。经过分析可能是页面被替换时,可能是os系统可能比较两者的地址是否互相以其中一个为前缀,若是就不回调。而UIWebView由于设置了HybridNSURLProtocol协议拦截,部分子页面通过canInitWithRequest回调,不经过didFinishNavigation回调。

但是我们的需求是要监控到这种页面变更,若是几个指定的首页页面不能显示返回按钮,若不是首页的页面请求需要显示返回按钮。

最好真的办法了,最终通过监控页面的变化来查看当前页面url来决定是否显示返回按钮。

下面是一个不完美的判断当前页面的方法,在我们的app测试时有小bug。

WKWebView的self.wkWebView.backForwardList.currentItem一般是当前页面(但是当一个tabbar页面有个子页面,可能这个地址并非时真实的当前网页地址,所以最好别用它),这个地址:NSString *url = [self.wkWebView.URL.absoluteString stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];一般是当前页面具体地址。

backForwardList是可以返回的页面请求地址。不过最好别直接读这个数组,或计算它的个数。因为页面变化时,这个数组会变化的。大家知道,当一个可变数组正变化时,另一个线程计算它的个数可能引起crash.

获取当前页面的地址最直接的方法是通过:self.wkWebView.URL.absoluteString直接获取。


问题是何时去读区当前页面的地址呢?由于WKWebView有一个成员对象scrollView,而UIScrollView的contentSize属性是可以通过KVO监控的。我们采用的响应式编程ReactiveCocoa这个库,通过这段代码监控页面尺寸的变化:

    [RACObserve(self.wkWebView.scrollView, contentSize) subscribeNext:^(id x) {
        @strongify(self);
        NSLog(@"xxxxxx:%@, nowTime:%lld毫秒", x, (long long)([[NSDate date] timeIntervalSince1970]*1000));
        [self updateNavigationItems:self.wkWebView.URL.absoluteString];
    }];

大家可以看到只要页面地址变化,这个KVO都能监控到,当然有的js页面是不断刷新的,这个KVO也能监控到,这个频率不是一般的快,大约20毫秒左右就会出现一次页面尺寸的变化,当然有的页面加载完成后变化几次就不会引起页面尺寸的变化了。页面变化那么快,也就是手机打开网页,电量下降飞快的原因吧?

2018-05-31 17:19:26.114506+0800 ArtEnjoymentWeChatAuction[61101:5370688] xxxxxx:NSSize: {320, 504}, nowTime:1527758366114毫秒
2018-05-31 17:19:26.114783+0800 ArtEnjoymentWeChatAuction[61101:5370688] current url:https://test/market/homepage.htm
2018-05-31 17:19:26.122076+0800 ArtEnjoymentWeChatAuction[61101:5370688] xxxxxx:NSSize: {320, 504}, nowTime:1527758366121毫秒
2018-05-31 17:19:26.122325+0800 ArtEnjoymentWeChatAuction[61101:5370688] current url:https://test/market/homepage.htm
2018-05-31 17:19:26.148894+0800 ArtEnjoymentWeChatAuction[61101:5370688] xxxxxx:NSSize: {320, 504}, nowTime:1527758366148毫秒
2018-05-31 17:19:26.149110+0800 ArtEnjoymentWeChatAuction[61101:5370688] current url:https://test/market/homepage.htm

根据这个KVO更新返回按钮的隐藏属性就可以。

-(void)updateNavigationItems:(NSString *)url
{
//    WKBackForwardListItem *currentItem = self.wkWebView.backForwardList.currentItem;
//    NSString *url = [currentItem.URL.absoluteString stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
    FLDDLogVerbose(@"current url:%@", url);
    self.navigationItem.leftBarButtonItem.customView.hidden = [AWGeneralFunction isHiddenWebBackButton:url];
}

-(BOOL)isHiddenWebBackButton:(NSString *)reqUrl
{
    if(isEmptyString(reqUrl))
    {
        return YES;
    }
    if(([reqUrl rangeOfString:homepageHtml].location != NSNotFound) && ([reqUrl rangeOfString:homepageExtendHtml].location == NSNotFound))
    {
        return YES;
    }
    else
    {
        return NO;
    }
}

具体代码见:https://blog.csdn.net/jia12216/article/details/80526133

用loadWebURLSring跳转到这个h5页面,加载远程js代码(h5地址上个页面提供)或在viewDidLoad加载指定的js地址(页面控制器指定h5地址)都可以,也可以加载本地代码(本地h5页面)。


上面的解决方案思路是对的,用KVO监控self.wkWebView.scrollView的contentSize,由于页面可能不断刷新,飞速的调用监控的处理函数(就是一个页面加载多次重复回调页面变化处理函数),这样功能是实现了,但是这样重复的操作实在没有必要。当时我找了几天我都没有找到更完美的解决方案。今天我偶然发现他的最佳解决方案。通过KVO监控URL的变化,并且就是像上面介绍的一个请求地址里包含两个Url也会回调,并且只回调一次。具体代码如下,在viewDidLoad函数中增加下面的代码:

[self.wkWebView addObserver:self forKeyPath:@"URL"options:NSKeyValueObservingOptionNew context:nil];

在observeValueForKeyPath函数中增加下面的代码,注意这个函数包含WKWebView的进度条的处理,要识别keyPath是@”URL”的情况判断,不判断乱使用app会crash的,测试用的是WKWebView控件:

//KVO监听进度条
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {

    if ([keyPath isEqualToString:NSStringFromSelector(@selector(estimatedProgress))] && object == self.wkWebView) {
        [self.progressView setAlpha:1.0f];
        BOOL animated = self.wkWebView.estimatedProgress > self.progressView.progress;
        [self.progressView setProgress:self.wkWebView.estimatedProgress animated:animated];

        // Once complete, fade out UIProgressView
        if(self.wkWebView.estimatedProgress >= 1.0f) {
            [UIView animateWithDuration:0.3f delay:0.3f options:UIViewAnimationOptionCurveEaseOut animations:^{
                [self.progressView setAlpha:0.0f];
            } completion:^(BOOL finished) {
                [self.progressView setProgress:0.0f animated:NO];
            }];
        }
    }
    else if([keyPath isEqualToString:@"URL"] && object == self.wkWebView)
    {
        [self updateNavigationItems:self.wkWebView.URL.absoluteString];
        NSLog(@"url == %@",self.wkWebView.URL.absoluteString);
    }
    else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

真搞不懂WKWebView为何不所有页面都走didStartProvisionalNavigation代理函数,这明显是它的bug,这不是害我们吗?


目录
相关文章
|
19天前
|
测试技术
用navigator.sendBeacon完成网页埋点异步请求记录用户行为,当网页关闭的时候,依然后完美完成接口请求,不会因为浏览器关闭了被中断请求。
用navigator.sendBeacon完成网页埋点异步请求记录用户行为,当网页关闭的时候,依然后完美完成接口请求,不会因为浏览器关闭了被中断请求。
|
12天前
|
JavaScript
WKWebView采用HybridNSURLProtocol协议拦截图片等资源预加载
WKWebView采用HybridNSURLProtocol协议拦截图片等资源预加载
16 1
|
12天前
|
存储 移动开发 缓存
多个WKWebView页面的cookie不共享问题及解决方案
多个WKWebView页面的cookie不共享问题及解决方案
21 0
|
11月前
|
JavaScript 前端开发 Go
页面前进、页面后退、页面跳转的方法
页面前进、页面后退、页面跳转的方法
|
11月前
|
存储 小程序 JavaScript
微信小程序路由跳转,API调用,页面传值
微信小程序路由跳转,API调用,页面传值
176 1
微信小程序路由跳转,API调用,页面传值
|
12月前
网络请求拦截302跳转,获取Location
网络请求拦截302跳转,获取Location
|
小程序
【微信小程序】wx.request请求后success回调的数据无法显示到页面上
【微信小程序】wx.request请求后success回调的数据无法显示到页面上
342 0
|
安全
WKWebView加载PDF屏蔽长按功能弹框
近期有个需求,需要在WKWebView加载PDF中,基于安全的理由,屏蔽【复制、选择全部】等功能弹窗
124 0
(uniapp跳转回显),A页面进入B页面,B页面返回A页面,并传值给A页面(技巧绝对好用慢慢看)
(uniapp跳转回显),A页面进入B页面,B页面返回A页面,并传值给A页面(技巧绝对好用慢慢看)
387 0
(uniapp跳转回显),A页面进入B页面,B页面返回A页面,并传值给A页面(技巧绝对好用慢慢看)