You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WKWebView的decidePolicyForNavigationAction加载新页面时未触发

WKWebView后续页面加载时decidePolicyForNavigationAction未触发的排查与解决

问题现象:WKWebView初始加载内容时,decidePolicyForNavigationAction方法正常工作,但后续加载新页面时该方法完全不触发。原本需要通过这个方法检测WebView是否要加载Web登录页,若检测到则登出用户并显示App原生登录页,但初始加载主屏幕后该方法再也没触发过。

相关实现代码:

- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    NSLog(@"decidePolicyForNavigationActionnavigationAction.request.URL.absoluteString:%@|", navigationAction.request.URL.absoluteString);
    
    
    if (kTestLocalHTML == 1) {
        decisionHandler(WKNavigationActionPolicyAllow);

        return;
    }
    
    
    NSURL *url = navigationAction.request.URL;
    if (url != nil) {
        
        
        //
        // TODO: Trim the url string below as well?
        //       No, because this is being trimmed already in the AppManager methods related to web view URL
        //
        
        NSString *urlString = url.absoluteString;
        WKNavigationType navType = navigationAction.navigationType;
        
        
        //
        // Display the club mobile native login page and log the user out
        // if the webview is about to load the blazor login page URL
        //
        if ([urlString isEqualToString:[AppManager getMobileLoginUrl]]) {
            self.appManager.userLoginDetails = nil;
            [self displayLoginPage];
            
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
        
        
        //
        // Check if link is tapped from webview and if URL string contains the
        // prepended identifier within the JavaScript injection which can be found in
        // "webViewDidFinishLoad" delegate method of webview
        //
        if (navType == WKNavigationTypeLinkActivated && [AppManager isWebViewURL_containsTargetBlank:urlString]) {
            
            //
            // Make sure to ignore the prepended identifier on the URL string
            // before opening it on an external browser
            //
            NSInteger targetBlankIdentifierLength = kIdentifier_TargetBlank.length;
            
            NSURL *url = [NSURL URLWithString:[urlString substringFromIndex:targetBlankIdentifierLength]];
            [[UIApplication sharedApplication] openURL:url];
            
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
        
        
        //
        // Check if url has custom scheme other than "http" & "https" (If is either one of the tags for Telephone "tel", Mail "mailto", SMS "sms", Web Calendar "webcal", etc.)
        //
        // Solution Reference: https://stackoverflow.com/questions/47040471/wkwebview-not-opening-custom-url-scheme-js-opens-custom-scheme-link-in-new-wind
        //      - (Answers of both "hstdt" & "mattblessed")
        //
        // NOTE: It seems loading telephone and mail links do not work when testing on an iOS simulator.
        //
        NSString *urlScheme = url.scheme;
        BOOL isCustomURLscheme = ( ! [urlScheme isEqualToString:@"http"] && ! [urlScheme isEqualToString:@"https"]);
        if (isCustomURLscheme) {
            
            NSURL *url = [NSURL URLWithString:urlString];
            [[UIApplication sharedApplication] openURL:url];
            
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
        
        
        //
        // Get reference to the current URL being requested so that if there
        // will be failure in loading the webview, we have a reference to that URL.
        //
        self.currentUrlRequest = navigationAction.request;
        
        
        // Check if web view URL contains a calendar file to download
        if ([AppManager isWebViewURL_containsCalendarDownload:urlString]) {
            
            //
            // Download and process calendar file either from Events or Dining module
            // and display "New Event" page where user can view its details, edit and save to device calendar
            //
            [self processCalendarFileAndDisplayEventPageWithURLrequest:self.currentUrlRequest];
            
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
        // Check if web view URL contains a PDF file attachment to download
        else if ([AppManager isWebViewURL_containsPDFAttachmentDownload:urlString]) {
            
            // Display document viewer page and pass few parameters for page customization (initial URL to load, navigation bar title)
            UIStoryboard *storyboard = [UIStoryboard storyboardWithName:kStoryboardName bundle:nil];
            UINavigationController *navigationController = [storyboard instantiateViewControllerWithIdentifier:kStoryboardID_OverlayBrowserNavigationController];
            OverlayBrowserViewController *vcOverlay = (OverlayBrowserViewController *)[navigationController topViewController];
            vcOverlay.initialURLtoLoad = urlString;
            vcOverlay.navigationBarTitle = [self getPDFDocumentNameFromURLString:urlString];
            [self presentViewController:navigationController animated:YES completion:nil];
            
            decisionHandler(WKNavigationActionPolicyCancel);
            return;
        }
    }
    
    decisionHandler(WKNavigationActionPolicyAllow);
}

可能的原因及解决方法

1. 代理对象未被正确持有

如果持有WKWebView的控制器被提前释放,或者WKNavigationDelegate的代理对象被销毁,后续的导航回调就会失效。

  • 检查WKWebView实例是否被当前控制器强引用,确保navigationDelegate设置正确:
    self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
    self.webView.navigationDelegate = self;
    [self.view addSubview:self.webView];
    

2. 页面使用SPA前端路由(无页面刷新)

如果加载的网页是单页应用(比如Blazor/Vue/React),页面跳转通过前端路由实现,这种情况不会触发decidePolicyForNavigationAction,因为没有发起新的HTTP请求,仅更新DOM。

  • 解决方法:注入JS监听路由变化,通过WKScriptMessageHandler通知原生代码:
    1. 配置WKUserContentController:
      WKUserContentController *userContentController = [[WKUserContentController alloc] init];
      [userContentController addScriptMessageHandler:self name:@"routeChange"];
      
      WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
      config.userContentController = userContentController;
      
      self.webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:config];
      self.webView.navigationDelegate = self;
      
    2. 在页面加载完成后注入监听JS:
      - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
          NSString *js = @"// 监听hash路由变化
                          window.addEventListener('hashchange', function() {
                              window.webkit.messageHandlers.routeChange.postMessage(window.location.href);
                          });
                          // 监听history模式路由变化
                          (function(history){
                              var pushState = history.pushState;
                              history.pushState = function(state) {
                                  window.webkit.messageHandlers.routeChange.postMessage(window.location.href);
                                  return pushState.apply(history, arguments);
                              };
                              var replaceState = history.replaceState;
                              history.replaceState = function(state) {
                                  window.webkit.messageHandlers.routeChange.postMessage(window.location.href);
                                  return replaceState.apply(history, arguments);
                              };
                          })(window.history);";
          [webView evaluateJavaScript:js completionHandler:nil];
      }
      
    3. 实现WKScriptMessageHandler接收路由变化并处理:
      - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
          if ([message.name isEqualToString:@"routeChange"]) {
              NSString *currentUrl = message.body;
              if ([currentUrl containsString:[AppManager getMobileLoginUrl]]) {
                  self.appManager.userLoginDetails = nil;
                  [self displayLoginPage];
              }
          }
      }
      

3. 未处理新开窗口的导航请求

如果网页中有target="_blank"的链接,WebView会尝试新开窗口,若未实现WKUIDelegate的相关方法,新开窗口的导航不会触发navigationDelegate回调。

  • 实现WKUIDelegate方法并设置代理:
    self.webView.UIDelegate = self;
    
    - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
        // 在当前WebView中加载新开窗口的请求
        if (!navigationAction.targetFrame.isMainFrame) {
            [webView loadRequest:navigationAction.request];
        }
        return nil; // 不创建新窗口
    }
    

4. URL匹配逻辑过于严格

代码中使用isEqualToString:进行精确匹配,如果Web登录页URL带有参数(比如token、查询参数),会导致匹配失败,误以为方法未触发。

  • 先检查NSLog是否有输出,确认方法是否真的未触发;若有输出,修改匹配逻辑为包含匹配:
    if ([urlString containsString:[AppManager getMobileLoginUrl]]) {
        self.appManager.userLoginDetails = nil;
        [self displayLoginPage];
        decisionHandler(WKNavigationActionPolicyCancel);
        return;
    }
    

内容的提问来源于stack exchange,提问作者Daniel98

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 22:30:48