美文网首页codeiOS程序犭袁iOS
WKWebView的使用和各种坑的解决方法(OC+Swift)

WKWebView的使用和各种坑的解决方法(OC+Swift)

作者: winann | 来源:发表于2016-07-01 15:53 被阅读66312次

    虽然WKWebView是在AppleWWDC 2014iOS 8OS X 10.10出来的,是为了解决UIWebView加载速度慢、占用内存大的问题。但是由于之前还要适配iOS7,又不想做两套加载页面(主要是因为懒),所以就没有使用。现在项目都适配iOS 8以上了,所以就开始使用WKWebView了,但是发现在使用的时候有好多坑,希望这篇文章能带大家绕过坑,更好的使用WKWebView

    这篇文章主要介绍了以下问题,方便小伙伴们查阅:

    1. WKWebView的基本介绍和使用
    1. WKWebViewJavaScript的交互
    1. 解决WKWebView加载POST请求无法发送参数问题

    WKWebView的基本介绍和使用

    WKWebView的几个代理方法

    WKWebView是苹果在iOS 8中引入的新组件,目的是给出一个新的高性能的WebView解决方案,摆脱过去 UIWebView的老、旧、笨重,特别是内存占用量巨大的问题,它使用Nitro JavaScript引擎,这意味着所有第三方浏览器运行JavaScript将会跟safari一样快。

    看到我这篇文章的小伙伴,对iOS的开发应该有一定的了解,肯定用过UIWebView,现在就用UIWebViewWKWebView的代理方法做一个对比。

    • 加载状态的回调(用来跟踪页面加载的过程(页面开始加载、加载完成、加载失败的方法),还可以决定是否跳转):

      1. 准备加载页面
            UIWebViewDelegate: - webView:shouldStartLoadWithRequest:navigationType
            WKNavigationDelegate: - webView:didStartProvisionalNavigation:
    
    2. **内容开始加载**`(view的过渡动画可在此方法中加载)`
    
            UIWebViewDelegate: - webViewDidStartLoad:
            WKNavigationDelegate: - webView:didCommitNavigation:
    
    3. **页面加载完成**`(view的过渡动画的移除可在此方法中进行)`
    
            UIWebViewDelegate: - webViewDidFinishLoad:
            WKNavigationDelegate: - webView:didFinishNavigation:
    
    4. **页面加载失败**
    
            UIWebViewDelegate: - webView:didFailLoadWithError:
            WKNavigationDelegate: - webView:didFailNavigation:withError:
            WKNavigationDelegate: - webView:didFailProvisionalNavigation:withError:
    

    此外,WKWebKit还有三个页面跳转的代理方法:

    • 页面跳转的代理
      1. 接收到服务器跳转请求的代理
            WKNavigationDelegate: - webView:didReceiveServerRedirectForProvisionalNavigation:
    
    2. **在收到响应后,决定是否跳转的代理**
    
            WKNavigationDelegate: - webView:decidePolicyForNavigationResponse:decisionHandler:
    
    3. **在发送请求之前,决定是否跳转的代理**
    
            WKNavigationDelegate: - webView:decidePolicyForNavigationAction:decisionHandler:
    

    WKWebView增加的属性

    1. WKWebViewConfiguration *configuration:初始化WKWebView的时候的配置,后面会用到
    2. WKBackForwardList *backForwardList:相当于访问历史的一个列表
    3. double estimatedProgress:进度,有这个之后就不用自己写假的进度条了

    WKWebView的使用

    OC代码:

        // 创建WKWebView
        WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
        // 设置访问的URL
        NSURL *url = [NSURL URLWithString:@"http://www.jianshu.com"];
        // 根据URL创建请求
        NSURLRequest *request = [NSURLRequest requestWithURL:url];
        // WKWebView加载请求
        [webView loadRequest:request];
        // 将WKWebView添加到视图
        [self.view addSubview:webView];
    

    Swift代码:

        // 创建WKWebView
        let webView = WKWebView(frame: UIScreen.mainScreen().bounds)
        // 设置访问的URL
        let url = NSURL(string: "http://www.jianshu.com")
        // 根据URL创建请求
        let requst = NSURLRequest(URL: url!)
        // WKWebView加载请求
        webView.loadRequest(requst) 
        // 将WKWebView添加到视图
        view.addSubview(webView)
    

    可以看到很简单,和UIWebView并没有多少差别,然而性能就刷刷刷的提上去了,是不是很爽呢?如果你只是简单的集成个Web页到App,这些已经够了。不过很多时候并没有那么简单,还需要处理各种东西,那么接着往后看。


    WKWebViewJavaScript的交互

    WebKit框架中,有WKWebView可以替换UIKitUIWebViewAppKitWebView,而且提供了在两个平台可以一致使用的接口。WebKit框架使得开发者可以在原生App中使用Nitro来提高网页的性能和表现,Nitro就是SafariJavaScript引擎,WKWebView不支持JavaScriptCore的方式但提供message handler的方式为JavaScript与Native通信。(这个引自天狐博客,更多的与UIWebView或者WKWebView的交互方法可以在这里看到。下面部分代码(例如JS)也是窃取这个作者的,尊重原著,所以把原博客地址放这里,与JS交互写的比我好多了。)

    Native调用JavaScript方法

    原生调用JavaScript的代码需要在页面加载完成之后,就是在 - webView:didFinishNavigation:代理方法里面
    OC代码:

    [webView evaluateJavaScript:@"showAlert('奏是一个弹框')" completionHandler:^(id item, NSError * _Nullable error) {
            // Block中处理是否通过了或者执行JS错误的代码
        }];
    

    Swift代码:

    webView.evaluateJavaScript("showAlert('奏是一个弹框')") { (item, error) in
                // 闭包中处理是否通过了或者执行JS错误的代码
            }   
    

    大家可以看到这段JS代码是最简单的弹出一个Alert的代码,后面WKWebView加载POST请求参数问题中还会有一个加载POST请求的JS代码,先不要管它了,请各位看官继续往后翻,看看JavaScript怎么调用Native的方法。

    JavaScript调用Native方法

    • JavaScript的配置

      JavaScript调用Native的方法就需要前端和Native的小伙伴们配合了,需要前端的小伙伴在JS的方法中调用:

      window.webkit.messageHandlers.NativeMethod.postMessage("就是一个消息啊");
      

      这行代码。请注意,这个NativeMethod是和App中要统一的,配置方法将在下面的Native中书写。

    • Native App的代码配置

      下面该Native的代码的配置了,细心的小伙伴可能已经发现了,创建WKWebView的时候,除了有- initWithFrame:方法外,还有一个高端的方法:- initWithFrame:configuration:方法。那句名言是谁说的来着:普通玩家选择推荐配置,高端玩家选择自定义配置,就当是我说的吧(那个拿鞋的把鞋穿上吧,我承认不是我说的😂)。这个方法就是用来自定义配置的,具体怎么自定义呢,童鞋们接着往下看吧。

      OC代码:

          // 创建配置
          WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init];
          // 创建UserContentController(提供JavaScript向webView发送消息的方法)
          WKUserContentController* userContent = [[WKUserContentController alloc] init];
          // 添加消息处理,注意:self指代的对象需要遵守WKScriptMessageHandler协议,结束时需要移除
          [userContent addScriptMessageHandler:self name:@"NativeMethod"];
          // 将UserConttentController设置到配置文件
          config.userContentController = userContent;
          // 高端的自定义配置创建WKWebView
          WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds configuration:config];
          // 设置访问的URL
          NSURL *url = [NSURL URLWithString:@"http://www.jianshu.com"];
          // 根据URL创建请求
          NSURLRequest *request = [NSURLRequest requestWithURL:url];
          // WKWebView加载请求
          [webView loadRequest:request];
          // 将WKWebView添加到视图
          [self.view addSubview:webView];
      

      Swift代码:

          // 创建配置
          let config = WKWebViewConfiguration()
          // 创建UserContentController(提供JavaScript向webView发送消息的方法)
          let userContent = WKUserContentController()
          // 添加消息处理,注意:self指代的对象需要遵守WKScriptMessageHandler协议,结束时需要移除
          userContent.addScriptMessageHandler(self, name: "NativeMethod")
          // 将UserConttentController设置到配置文件
          config.userContentController = userContent
          // 高端的自定义配置创建WKWebView
          let webView = WKWebView(frame: UIScreen.mainScreen().bounds, configuration: config)
          
          // 设置访问的URL
          let url = NSURL(string: "http://www.jianshu.com")
          // 根据URL创建请求
          let requst = NSURLRequest(URL: url!)
          // 设置代理
          webView.navigationDelegate = self
          // WKWebView加载请求
          webView.loadRequest(requst)
          
          // 将WebView添加到当前view
          view.addSubview(webView)
      

      可以看到,添加消息处理的handlername,就是JavaScript中调用时候的NativeMethod,这两个要保持一致。请把URL换成你自己的。

      请注意第6行的代码配置当前ViewControllerMessageHandler,需要服从WKScriptMessageHandler协议,如果出现警告⚠️,请检查是否服从了这个协议。

      注意!注意!注意:上面将当前ViewController设置为MessageHandler之后需要在当前ViewController销毁前将其移除,否则会造成内存泄漏。

      移除的代码如下:

      OC代码:

      [webView.configuration.userContentController removeScriptMessageHandlerForName:@"NativeMethod"];
      

      Swift代码:

      webView.configuration.userContentController.removeScriptMessageHandlerForName("NativeMethod")
      

      请注意这个Name和上面创建WKWebView的配置中注册的名字是一样的,要保持对应。

      好了,现在万事俱备,只欠东风了。东风是什么呢,就是该在哪儿处理。可以看到WKScriptMessageHandler的协议里面只有一个方法,就是:

      - userContentController:didReceiveScriptMessage:
      

      相信聪明的你已经猜到了。是的,就是在这个代理方法里面操作:如果JavaScript执行已经写好的:window.webkit.messageHandlers.NativeMethod.postMessage("就是一个消息啊");这行代码,这个代理方法就会走,并且会有个WKScriptMessage的对象,这个WKScriptMessage对象有个name属性,拿到之后你会发现,就是我们注册的NativeMethod这个字符串,这时候你就可以手动调用Native的方法了。如果有多个方法需要调用的话怎么办,看到JavaScriptpostMessage()方法有一个参数了没有,可以根据这里的参数来区分调用原生App的哪个方法。
      代码很简单,就不写了。什么?你说你还需要写?好吧,那我还是贴出来吧:

      OC代码:

          - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
              // 判断是否是调用原生的
              if ([@"NativeMethod" isEqualToString:message.name]) {
                  // 判断message的内容,然后做相应的操作
                  if ([@"close" isEqualToString:message.body]) {
              
                  }
              }
          }
      

      Swift代码:

          func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {
              // 判断是否是调用原生的
              if "NativeMethod" == message.name {
                  // 判断message的内容,然后做相应的操作
                  if "close" == message.body as! String {
                  
                  }
              }
          }
      

      上面的方法就可以获取到JavaScript发送的Message了,JavaScript可以这样调用:window.webkit.messageHandlers.NativeMethod.postMessage("close");,这时候上面的代理方法的两个if判断都能通过,不同的操作可增加里面的if语句的分支判断message的内容来进行不同的Native代码的调用,也就是JavaScriptpostMessage方法的参数的不同来区分不同的操作。

      好了,现在WKWebViewJavaScript的简单交互你也会了。用WKWebView的时候貌似也还算开心。但是不要高兴的太早,下面就要有坑了。


    解决WKWebView加载POST请求无法发送参数问题

    也许你用UIWebView加载过POST请求的页面,感觉并没有什么难点或者需要注意的地方,那真的是图样图森破了,因为我也这样天真过。直到我踩了很多坑之后,我才发现梦想与现实之间的差别,不过没关系,我又要说另一句名言了:没有挖不到的墙角...,咳咳咳,说错了,请重新来BGM,跟我一起说:没有解决不了的Bug,只有不努力的码农!(各位架构师、高级开发工程师请手下留情,我说的码农是我😂)

    来来来,先来一发POST请求加载WebView。你会说,这还不easy?下面就来一个,走起:
    OC代码:

        // 创建WKWebView
        WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
        // 设置访问的URL
        NSURL *url = [NSURL URLWithString:@"http://www.example.com"];
        // 根据URL创建请求
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        // 设置请求方法为POST
        [request setHTTPMethod:@"POST"];
        // WKWebView加载请求
        [webView loadRequest:request];
        // 将WKWebView添加到视图
        [self.view addSubview:webView];
    

    Swift代码:

        // 创建WKWebView
        let webView = WKWebView(frame: UIScreen.mainScreen().bounds)
        // 设置访问的URL
        let url = NSURL(string: "http://www.example.com")
        // 根据URL创建请求
        let requst = NSMutableURLRequest(URL: url!)
        // 设置请求方法为POST
        requst.HTTPMethod = "POST"
        // WKWebView加载请求
        webView.loadRequest(requst)
        // 将WKWebView添加到视图
        view.addSubview(webView)
    

    这样确实加载POST请求的网页成功了(注意请把链接换成自己的),你一定露出了得意的笑容。但是骚年,不要高兴的太早,这只是一个简单的POST请求,还没有添加参数呢。于是乎,你又说:那更简单,在第9行插入如下代码即可(比方说这个接口是登录):

    OC代码:

        // 设置请求参数
        [request setHTTPBody:[@"username=aaa&password=123" dataUsingEncoding:NSUTF8StringEncoding]];
    

    Swift代码:

        // 设置请求参数
        requst.HTTPBody = "username=aaa&password=123".dataUsingEncoding(NSUTF8StringEncoding)
    

    这种方法在UIWebView里面是没有问题的,所以你认为在这里也应该是没有问题的。从理论上讲应该是这样的,但是我要恭喜你了,这是WKWebView的Bug,让你给碰到了。这里写的POST请求没有问题,但是就是不会把这两个参数传上去的,不信你可以试试(截止我发表这篇博客的日期,iOS 9.3并没有修复此问题)。

    好了,不废话了(其实已经说了很多废话了),下面看解决办法(如果你需要适配iOS 8请直接使用方法2):

    1. 使用NSURLSession发送一个请求,然后把请求下来的数据当作本地HTML加载
    2. 使用JavaScript解决WKWebView无法发送POST参数问题

    1. 使用NSURLSession解决WKWebView无法POST参数的问题(性能和结果都可能有问题,不推荐使用)

    当发现POST无法传递参数的时候,我首先想到的是换个方法来,就是用一般的请求方式:NSURLSession发送请求,然后把接收到的数据转化成字符串,然后再用WKWebView加载。大家可能已经看出来了,需要把整个网页放到内存中或着放到本地然后再加载,所以肯定消耗内存呀。下面贴代码吧:

    OC代码:

        // 创建WKWebView
        WKWebView *webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
        // 将WKWebView添加到当前View
        [self.view addSubview:webView];
        // 设置访问的URL
        NSURL *url = [NSURL URLWithString:@"http://www.example.com"];
        // 根据URL创建请求
        NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
        // 设置请求方法为POST
        [request setHTTPMethod:@"POST"];
        // 设置请求参数
        [request setHTTPBody:[@"username=aaa&password=123" dataUsingEncoding:NSUTF8StringEncoding]];
        
        // 实例化网络会话
        NSURLSession *session = [NSURLSession sharedSession];
        // 创建请求Task
        NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
            
            // 将请求到的网页数据用loadHTMLString 的方法加载
            NSString *htmlStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
            [webView loadHTMLString:htmlStr baseURL:nil];
        }];
        // 开启网络任务
        [task resume];
    

    Swift代码:

        // 创建WKWebView
        let webView = WKWebView(frame: UIScreen.mainScreen().bounds)
        // 设置访问的URL
        let url = NSURL(string: "http://www.example.com")
        // 根据URL创建请求
        let requst = NSMutableURLRequest(URL: url!)
        // 设置请求方法为POST
        requst.HTTPMethod = "POST"
        // 设置请求参数
        requst.HTTPBody = "username=aaa&password=123".dataUsingEncoding(NSUTF8StringEncoding)
        // 将WKWebView添加到视图
        view.addSubview(webView)
        
        // 实例化网络会话
        let session = NSURLSession.sharedSession()
      
        // 创建请求Task
        let task = session.dataTaskWithRequest(requst) { (data, response, error) in
            webView.loadHTMLString(String(data: data!, encoding: NSUTF8StringEncoding)!, baseURL: nil)
        }
        task.resume()
    

    当你用iOS 9以上的设备的时候,貌似完全没有一点问题,只是需要请求下来再放而已。但是注意前提条件:iOS 9,当你用iOS 8的时候,发现你的网页的样式和JavaScript事件全部没有了。是不是有一种呵呵的冲动,那你就尽情呵呵吧。如果你要适配iOS 8,那么这个方法也不符合你的气质。

    其实这个东西和加载本地网页无法加载CSS样式和JS一样,如果你也加载本地HTML文件出现问题,请查看Jay神WKWebView使用遇到的坑。尽给别人打广告了,呵呵,声明一下啊:我跟这些人木有关系,只是为了方便大家查阅而已,谁让我那么的大公无私呢😂。

    好了,好了,来看一个更好的解决办法吧:

    2. 使用JavaScript解决WKWebView无法发送POST参数问题

    开始之前我先说一下实现思路,方便大家理解,如果出错了也能知道错误的地方:

    1. 将一个包含JavaScriptPOST请求的HTML代码放到工程目录中
    2. 加载这个包含JavaScriptPOST请求的代码到WKWebView
    3. 加载完成之后,用Native调用JavaScriptPOST方法并传入参数来完成请求
    1. 创建包含JavaScriptPOST请求的HTML代码

      相关代码:

      <html>
      <head>
          <script>
              //调用格式: post('URL', {"key": "value"});
              function post(path, params) {
                  var method = "post";
                  var form = document.createElement("form");
                  form.setAttribute("method", method);
                  form.setAttribute("action", path);
      
                  for(var key in params) {
                      if(params.hasOwnProperty(key)) {
                          var hiddenField = document.createElement("input");
                          hiddenField.setAttribute("type", "hidden");
                          hiddenField.setAttribute("name", key);
                          hiddenField.setAttribute("value", params[key]);
      
                          form.appendChild(hiddenField);
                      }
                  }
                  document.body.appendChild(form);
                  form.submit();
              }
          </script>
      </head>
      <body>
      </body>
      

    </html>
    ```
    将这段代码拷贝下来,然后粘贴到文本编辑器中,名字可以随意起,比方说保存为:JSPOST.html,然后拷贝到工程目录中,记得选择对应的Target和勾选Copy items if needed(默认应该是勾选的)。这时候,就可以用这段JavaScript代码来发送带参数的POST请求了。

    1. 将对应的JavaScript代码通过加载本地网页的形式加载到WKWebView

      OC代码:

      // JS发送POST的Flag,为真的时候会调用JS的POST方法(仅当第一次的时候加载本地JS)
      self.needLoadJSPOST = YES;
      // 创建WKWebView
      self.webView = [[WKWebView alloc] initWithFrame:[UIScreen mainScreen].bounds];
      //设置代理
      self.webView.navigationDelegate = self;
      // 获取JS所在的路径
      NSString *path = [[NSBundle mainBundle] pathForResource:@"JSPOST" ofType:@"html"];
      // 获得html内容
      NSString *html = [[NSString alloc] initWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
      // 加载js
      [self.webView loadHTMLString:html baseURL:[[NSBundle mainBundle] bundleURL]];
      // 将WKWebView添加到当前View
      [self.view addSubview:self.webView];
      

      Swift代码:

      // JS发送POST的Flag,为真的时候会调用JS的POST方法(仅当第一次的时候加载本地JS)
      needLoadJSPOST = true
      // 创建WKWebView
      webView = WKWebView(frame: UIScreen.mainScreen().bounds)
      //设置代理
      webView.navigationDelegate = self
      // 获取JS路径
      let path = NSBundle.mainBundle().pathForResource("JSPOST", ofType: "html")
      // 获得html内容
      do {
          
          let html = try String(contentsOfFile: path!, encoding: NSUTF8StringEncoding)
          // 加载js
          webView.loadHTMLString(html, baseURL: NSBundle.mainBundle().bundleURL)
      } catch { }
      // 将WKWebView添加到当前View
      view.addSubview(webView)
      

      这段代码就相当于把工程中的JavaScript脚本加载到WKWebView中了,后面就是看怎么用了。(请注意换成您的文件名)

    2. Native调用JavaScript脚本并传入参数来完成POST请求

      还记得 WKWebView和JavaScript的交互这一节嘛?现在该Native调用JavaScript了,如果忘记了,请往前翻温故一下:- webView:didFinishNavigation:代理表明页面已经加载完成,我们在这里操作,下面上代码:

      OC代码:

      // 加载完成的代理方法
      - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
          // 判断是否需要加载(仅在第一次加载)
          if (self.needLoadJSPOST) {
              // 调用使用JS发送POST请求的方法
              [self postRequestWithJS];
              // 将Flag置为NO(后面就不需要加载了)
              self.needLoadJSPOST = NO;
          }
      }
      
      // 调用JS发送POST请求
      - (void)postRequestWithJS {
          // 发送POST的参数
          NSString *postData = @"\"username\":\"aaa\",\"password\":\"123\"";
          // 请求的页面地址
          NSString *urlStr = @"http://www.postexample.com";
          // 拼装成调用JavaScript的字符串
          NSString *jscript = [NSString stringWithFormat:@"post('%@', {%@});", urlStr, postData];
      
          // NSLog(@"Javascript: %@", jscript);
          // 调用JS代码
          [self.webView evaluateJavaScript:jscript completionHandler:^(id object, NSError * _Nullable error) {
          
          }];
      }
      
      

      Swift代码:

      // 加载完成的代理方法
      func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {
          // 判断是否需要加载(仅在第一次加载)
          if needLoadJSPOST {
              // 调用使用JS发送POST请求的方法
              postRequestWithJS()
              // 将Flag置为NO(后面就不需要加载了)
              needLoadJSPOST = false
          }
      }
      // 调用JS发送POST请求
      func postRequestWithJS() {
          // 发送POST的参数
          let postData = "\"username\":\"aaa\",\"password\":\"123\""
          // 请求的页面地址
          let urlStr = "http://www.postexample.com"
          // 拼装成调用JavaScript的字符串
          let jscript = "post('\(urlStr)', {\(postData)});"
          // 调用JS代码
          webView.evaluateJavaScript(jscript) { (object, error) in
              
          }
      }
      

      好了,到目前为止你的请求就发出去了。相信后面的版本会解决这个问题,但是现在你要用的话也得有办法,谁让已经入了Apple的坑呢,谁让UIWebView太不给力了呢.


    写在最后:
    当时选择WKWebView就是为了提高性能,但是没有想到遇到这么多坑,从看iOS 9才解决了iOS 8无法加载本地样式的问题,有时候苹果解决问题的速度还有略慢的,到现在POST请求参数都发不出去也真是不应该。不过没办法,先解决了,说不定iOS 10 出来之后解决了呢。(我虽然有iOS 10的设备,但是我还没有测试,感兴趣的小伙伴们可以试试)。大家如果有什么问题,欢迎留言提问。谢谢支持!


    相关文章

      网友评论

      • SDBridge:分享一个Demo. WKWebView 监听JS端的所有的console.log日志
        https://www.jianshu.com/p/09e4799c5328
        https://github.com/housenkui/WKWebView-Console/
      • 冰三尺:楼主我遇到一个奇怪的问题,就是加载服务器反返回的html标签,其实就是一段文字加图片,有时能加载成功,有时不能加载成功,是需要对服务器返回的标签进行UTF-8 编码吗?
      • 云逸枫林:今天遇到这个问题了 真·坑,感谢楼主
      • Beyond无状态:楼主 userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message这个代理方法不走是什么原因啊 协议也遵循了
        一路向北客:@多醋多辣 好像不是这个问题 我这边是后台问题解决了
        Beyond无状态:@一路向北客 我加了一行_checkView.configuration.preferences.javaScriptCanOpenWindowsAutomatically = YES;
        不知道为什么就可以了....
        一路向北客:我也遇到了 请问解决了啊吗
      • LD_左岸:如果我用WKWebView加载 含有大量图片的pdf的http链接

        滑动webView

        异常的卡顿 卡的很!
        楼主有解决之道否:pray:
      • SoldOut:写的很详细,非常感谢。
      • 从此你不再颠沛流离:楼主,我这有个网页是汽车之家的,其中的图片加载不出来,不知道是不是他们的js没走,你给看一下
        http://61.240.128.76/cont_v8.8.5/content/news/newscontent-pm1-n915302-t0-rct1-ish1-ver.json

        在header里面价格 Host : cont.app.autohome.com.cn
        在网页上没问题,
        手机WKWeview加载不出来....
      • UncleFool:你好,请教一个问题。js端在调用window.webkit.messageHandlers.AppModel.postMessage()时,需要iOS端向js端传递一个值。请问上面这个方法可以接收返回值吗,iOS端应该怎么处理呢?
        做个有趣的程序员:@多醋多辣 window.webkit.messageHandlers.AppModel.postMessage(NULL或者其他参数),里面不能为空什么都不写
        Beyond无状态:我这个方法为啥不执行呢 兄弟
      • 5d79867452ca:原生app加载H5页面,页面中的line-height样式失效导致布局样式错乱,行高变得很大,这个问题怎么解决,求指教
      • panv587:js传参数,如果参数是中文,服务端显示乱码(服务端已经是UTF8格式)
      • f582eef5ecf6:你好作者,我有加载问题想问下你,可以加下QQ吗921903719
      • 木子影:-(WKWebView*)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures 楼主,能不能讲解一下这个方法以及坑,后台新开页面用表单数据,但是后台却拿不到,请求体走丢了
      • langkee:666:+1:
      • 什么_呢:想问一下楼主,因为我们产品说现在用wkwebview加载网页他还是觉得慢,大概要5秒这样才能假装出来,问有没有什么方法可以让加载网页的速度更快的,我网上看了一些资料说要写h5端的去改,客户端不用改,但是他们说在页面里面再打开页面又很快,只有从客户端跳到页面的时候才慢
        童趣小窝:有个问题请教一下楼主,我在- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler这个方法里只能拦截到get请求,为什么拦截不到post请求呢?
        什么_呢:加载,字打错了
      • 聪zero:请问wkwebview如果加载本地html,然后本地html页面发出的https请求如何进行自签名认证,afn的https请求用自签名是是没问题的,wkwebview加载https连接也是没问题的,就是这个加载本地资源,然后再发出的https请求就不知道怎么处理了,不处理就连接失败
      • 一剑开天门:楼主。我qq1525209241 加下好友交流一下:relaxed:
      • 一剑开天门:楼主啊。今天突然遇见一个大坑。html页面图片多的话,有一部分不能被加载出来:flushed:
      • Mrqwer:楼主,同一个界面,加载两个wkwebView,-(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;方法只执一次行一次,这个怎么解决
        Beyond无状态:兄弟 我这个方法不执行是怎么回事啊
      • 319acb86b557:不知道楼主有没有碰到过这个问题,我在viewWillAppear中 add userContentController , 在viewWillDisappear中removeScriptMessageHandler,会出现一种很奇怪的问题,第一次这个页面是没有问题的,但是从其他页面返回到这个页面,就不会走userContentController的代理了,返回页面时,add userContentController的方法还是走了的,也没报错。
      • 伦敦乡下的小作家:读完了,楼主肯定是个段子手:grin: ,学到很多,谢谢咯。
      • 经天纬地:有demo 吗
      • 经天纬地:有demo 吗
      • drmi:大神能留个联系方式吗,有些问题交流一下,主要关于加载方面
      • 救兵请来的猴子:我的项目中在加载本地的html时候,html内部会发送post请求.参数是放在header中,请问这个怎么能够解决
      • _源:你好,我使用了你所说的使用JavaScript解决WKWebView无法发送POST参数问题。可以在请求头里拦截到参数了,但是,我们后台对设备有个判断,需要我们在请求头里加上一个设备样式。那该如何做呢?
      • 带有bug的文艺青年:你好,看了你的方法之后:程序运行到这个地方之后: webView.loadHTMLString(html, baseURL: Bundle.main.bundleURL)
        报错:2017-05-06 19:25:04.883512 AotuXian[710:264501] fatal error: unexpectedly found nil while unwrapping an Optional value

      • 云抱住阳光太阳没放弃发亮:请问把网页保存到本地后加载速度会变快吗?
      • vision_colion:object=(null),error=Error Domain=WKErrorDomain Code=4 "A JavaScript exception occurred" UserInfo=0x7f9fde704830 {NSLocalizedDescription=A JavaScript exception occurred}楼主,好像有点问题
      • 285b71b2e49e:你好,我想请教一下,使用WKWebView 的时候,加载个页面,然后回到后台一段时间,玩其他东西,一段时间后打开APP发现WKWebView变白屏了,但是WKWebView加载的网页内容还是可以点击的,操作都能点,就WKWebView要init一下才能显示,问,这个白屏要怎么判断
      • 月光变成淡蓝色:大神,我刚用wk替换了uiwebview ,为什么加载速度反而慢了呢?
      • 810b938455d7:"上面将当前ViewController设置为MessageHandler之后需要在当前ViewController销毁前将其移除,否则会造成内存泄漏",请问在arc中怎么知道什么时候控制器会被销毁?
        5429695280dc:@xohome 谢谢:smile:
        f1badf52f473:同样是dealloc,只是ARC不需要super dealloc
      • 47fc41885eee:楼主,我加载出来为什么是空白的页面呀
        First灬DKS:请问空白页这个问题解决了吗?我的在iOS8上面也是空白页
      • 口袋海贼王_:怎么刷新啊。
      • 伯牙呀:if (_webView.canGoBack) {
        [_webView goBack];
        }

        如果这样返回的话,你加在webView:didCommitNavigation上的过渡动画会再走一遍的,这个怎么办?
      • 羽之_HB:请问wkwebview加载后的网页里自带的跳转按钮点击无效怎吗解决
      • 吴德馨:我用 WK加载的网页上有个按钮,点击后会跳出一个灰色界面和ActivityIndicator ,但是似乎是代理没设置没有加载ActivityIndicator出来,请问这个如何解决.
      • 吴德馨:请问 WKWebView 是否会自带一个 bar (这个 bar 是在网页上选择时间弹框出现的时候出现的) ,bar 上有确定确定按钮和上下箭头,但是点击其他位置 bar 又会消失.
      • objcat:感谢分享~ :blush:
      • Yc雨辰:请问下,如果我load的第一个界面就有POST参数,这中情况怎么处理呀?
      • 李乾坤David:楼主你好。我们的代码IOS前端和HTML都是按照天狐里写的!天狐Demo在IOS9和IOS10都没问题。但是我自己的在IOS8和IOS9都有问题在IOS10没有问题!IOS8在HTML上的错误是Can't find variable:callOC。
      • ReturnNil:这里具体post请求参数怎么灵活配置
      • initial_J:iOS 8
      • ReturnNil:我想请教一下 为什么没有网络时候用wk加载时 不会走- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error这个代理方法
      • 9cf8cb7150c9:楼主,加载了h5,h5内有表单提交,怎么才能参数值不丢失
      • c471f18d2f00:大神,麻烦问下我现在遇到了一个诡异的问题,按照正常流程走,我点击H5页面上的按钮,不走- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)
        这个方法,但换一个测试用的H5页面,就会走这个方法.能麻烦分析下问题可能出在哪吗?
      • chenfanfang:不错的文章,学习了
      • 1db2d8bf3912:感谢分享
      • 6ea8c6a1d65d:大神请教个问题啊。当我调用- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler 这个方法抓取navigationAction.request参数时,网页如果有异步请求就加载不出来,导致网页部分内容缺失,但我注销这个方法时,是可以正常加载出来的,跪求解决方法。 :sob:
        f1badf52f473:decidePolicyForNavigationAction拦截不到所有请求的。 你如果要拦截所有的请求,可以自己注入js,js重写XMLHttpRequest对象,拦截ajax请求,然后通过handle交互传递给native
      • 1b05ef2b4848:请教下楼主 咱们用wkwebview的这个代理方法 接受前端的调用
        “- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message”
        比如说前端想跟客户端获取用户信息的话 客户端接收到信息之后 该怎么返回相应的信息呢?
        之前uiwebview 我们可以直接retuan相应的信息
      • 超_iOS:wk post 现在还是无法解决,走过楼主大神的坑,我默默地还是用回uiwebview了. :joy:
      • YY_Lee:你好,用WKwebview加载网页,后台取不到sessionID,显示空白页有遇到过吗,我是第一次用WKwebview :persevere:
        Caiflower:@Gradient sessionid 不同步
      • 始于__初见: 您好,我刚刚使用WKWebview,显示都没问题,但是控制台会输出(没有规律):
        Could not signal service com.apple.WebKit.WebContent: 113: Could not find specified service
        Could not signal service com.apple.WebKit.Networking: 113: Could not find specified service
        偶兜key
        在没老之前:请问解决了吗?我也碰到这个问题了,看着一行行的打印,好烦啊 :cold_sweat:
        始于__初见:@Caiflower 没有呢 正常使用没有问题
        Caiflower:@始于__初见 解决了吗,我这也这样...
      • Arthurcsh:不错
      • 9d426ee34834:假如后台返回的是 HTML 那种类型字符串! 通过加载可以显示视图,那怎么处理里面点击时间呢 ?
      • 哼哈猿:楼主 按照给的实例写 初始化的时候加载本地HTML 然后加载完成之后post 但是post只是发送了post请求出去 怎么把那个我要post请求的网页也加载出来呢
        哼哈猿:@笑你个大麻花 是我自己的问题 真是尴尬 按照楼主的代码 来写post是阔以的 多谢!
      • 7408b7500a58:楼主知道怎么解决wkwebview 时而适配成功时而适配失败的问题么
      • 7408b7500a58:楼主逗逼大神一个鉴定完毕,哈哈哈
      • 从此你不再颠沛流离:- (nullable WKNavigation *)loadHTMLString:(NSString *)string baseURL:(nullable NSURL *)baseURL;

        如果加载本地html文件,文件在document中,baseURL是什么啊
        f1badf52f473:通过URL载入页面,内部的相对地址资源就会依赖原始的URL,你加载html字符串,没有原始URL信息,HTML字符串内的相对地址就需要你手动指定,就是这个baseURL参数。 比如你载入的HTML内部引用了js和css,那你需要给它指定载入URL
      • Alanxx:WKwebview 微信第三方登陆 不保存cookie怎么解决的呢
        Caiflower:@maxAlan 手动保存,然后加载时注入cookie
      • 鱼与熊掌不能兼得:不错,总结的很好
      • 狗不理火锅:大神,我用了WKWebView之后,感觉比UIWebView感觉更慢,更卡是怎么回事的啊?
      • fero2004:wkwebview会出现白屏的情况,貌似在打开系统拍照界面的时候会容易出现.不知道是什么问题.
      • 天清水蓝:使用楼主的第二种方式通过JavaScript实现了WKWebView的POST请求,但是同时还需要用`WebViewJavascriptBridge`这个库与前端交互。但是前端无法调起APP的注册事件呀,敢问这种情形楼主试过吗? :cold_sweat:
      • CoderFarmer:大神你好,请问下WKWebview怎么屏蔽广告啊 网上搜的都是UIWebview的
      • 小杨的app:怎么获取内容的高度啊
      • dd1d1568f067:楼主你的userContent.addScriptMessageHandler(self, name: "NativeMethod")行代码中,如果用self。它的内存不会释放的,测试环境为iOS10,我在另一篇文章上看到的是自创了一个代理,不知道你是怎么让其内存释放的?
      • z_hy:大神,在JS 通过 window.webkit.messageHandlers.NativeMethod.postMessage("就是一个消息啊"); 调用 Natvie 代码后,需要拿到返回值的问题怎么处理?
      • 3091cb953b66:请问楼主和各位亲们,有人遇到在iOS10上,iPad用WKwebView加载网页,结果网页的控件和字体都变得很大吗?哪怕加载百度首页都会。可是在iOS9这些都不会,而且只要iPad会这样。困惑,求助
      • 怎样m:有什么办法可以获取到网页中form提交的post数据吗?这个post数据是网页中做跳转时请求的,外面并不知道 我现在加载一个支付界面 就是post提交 数据全部丢掉了 请教大神
        民哥:我也有这个问题 你解决了吗
      • aa2e0aba1c58:关于 WKWebView 支持window.open()与alert()的问题,又遇到的吗
      • b7947f6d123c:有什么办法可以获取到网页中form提交的post数据吗?这个post数据是网页中做跳转时请求的,外面并不知道
        民哥:你解决了吗 我也遇到这个问题
      • F森:WKWebView有什么其他解决方案解决加载缓存数据吗
        winann:@F森 目前还没找到
      • 7972dbfe2596:大神 用WKWebView加载网站 文字正常 图片显示不全怎么办啊 :cold_sweat:
        winann:@7972dbfe2596 显示不全应该是网页的问题吧
      • 张云龙:http://ad.gaeamobile.net/?r=api&auth=OeFu2FjF
        这个如何跳转到APP Store?
        winann:@张云龙 判断URL 用openURL跳转
      • cbf2d993da7a:大神,我把webView替换了WKWebView后返回前一页就会崩到内存怎么回事啊?大神遇到过么?其它代码都没动,web的数据能加载出来。
        winann:@_奋斗ing_ 👌
        cbf2d993da7a:解决了,谢谢哈,是ScrollView协议强引用问题
        winann:@_奋斗ing_ 有崩溃日志嘛?
      • 371429183029:大神 求教一下, 我加载的是别的公司的网址 只能用url网址加载, 没办法在JS中加入 window.webkit.messageHandlers.NativeMethod.postMessage("就是一个消息啊");
        怎么才能抓取 网页中的内容
        winann:@月影九重 简单使用的话,你看看这个方法:evaluateJavaScript
        371429183029:之前的UIWebView 有stringByEvaluatingJavaScriptFromString 方法 可以抓取, 现在的WKWebView 有没有类似的方法, 求楼主推荐一些第三方库 我去找找
        winann:@月影九重 你用什么第三方的库试试,这个只是简单的交互,如果复杂的话,完全不能符合要求
      • 092c20805794:膜拜楼主
      • 我叫阿水:``window.webkit.messageHandlers.NativeMethod.postMessage("就是一个消息啊");``这行代码就写在js某个function(例如某个按钮的function)中吗??遵守了协议,设置了代理,但代理方法根本不会走。。。 :flushed: 怎么解?
        winann:@YioMidd 注册好的名字要和JS里面的调用的名字一样的才好,可以查一下前端的代码是否正确调用
        我叫阿水:@winann 我有注册,也有遵循协议,但是真的不走。。因为我这方面我不熟悉,前端的哥们也是做安卓兼顾的,也是不太懂,我觉得应该是前端代码的问题吧。。。。毕竟你的demo里那句简单的js代码,是可以的
        winann:@YioMidd 可以的,只要执行这行代码就可以了,记得要注册事件
      • 雨影:调用JS的方法一直报Error -> Error Domain=WKErrorDomain Code=4 "发生 JavaScript 异常" UserInfo={NSLocalizedDescription=发生 JavaScript 异常}这个错误,
        KCoder:@雨影 😂我也遇到这个问题,我是先加载网页链接,在`didFinishNavigation`代理中调了段js,传了参数,一直报这个错误,你们怎么看
        雨影:@winann 找到原因了,调用了js就不需要再调用loadrequest了
        winann:@雨影 看一下是否JS有你要调用的方法,调用的格式是否正确
      • 8290a89231af:楼主 , 首先膜拜一下, 但是 我按照你的方法来滴 , 为啥一直不走这个方法- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message;
        导致JS调用不了我的代码.可否指点一下.谢谢 了 :smile:
        winann:@cheng080010 博客中有写:作为message 的参数传进去
        8290a89231af:@winann 多谢楼主指点, 已经可以了, 但是如果web想传参数的话window.webkit.messageHandlers.self.postMessage("args1");JS里的应该怎么写?
        请楼主帮我看看
        winann:@cheng080010 看一下是否服从协议、是否添加handler ,可以先复制代码运行看看。更详细的内容可以看看这个博客:http://www.skyfox.org/javascript-ios-navive-message.html
      • 6b4128837278:NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:streamURL]];
        [_recWebView loadRequest:request];是这样的,我只需要加载网页,不需要发送参数,上线前在模拟器target8.0上运行了没问题(ps:当时没有8.0版本的手机可以真机调试用),上线了,后面遇到一台真机ios8.3却加载不了一片空白,请问wkwebview在8.0版本上运行必须要发送请求,然后把接收到的数据转化成字符串,然后再使用WKWebView加载吗?直接 loadRequest:不行吗?
        First灬DKS:你好,请问在iOS8.3上面空白的这个问题,你是怎么解决的呢?找了好久都没有找到原因呢!:joy:
        6b4128837278:@winann 谢谢 :smile: 只有继续去找其他的原因
        winann:@6b4128837278 不post 参数的话,直接loadRequest就好了,没有问题的
      • 8d7f845d2cde:谢谢啊,不过怎么判断移除成功了呢?
        f1badf52f473:@爱吃草莓的Rock 你这样很危险,键盘通知退出如果没有移除,首先确定你的对象释放了没有内存泄露,下次其它实例弹出键盘 在某些IOS版本系统上会崩溃。 不是所有版本对observer都是弱引用
        8d7f845d2cde:@winann 我还添加了一个键盘弹出的通知 都没有移除啊 并没有崩啊
        winann:@爱吃草莓的Rock 没有移除会崩溃的
      • 8d7f845d2cde:您好,WKWebview加载HTMLString是怎么弄的呀,我是把WKWebview放在TableView的一个cell上,所以还要获得到这个WKWebview的高度,[self.wkWebView loadHTMLString:tempHtml baseURL:baseURL]; 我这句执行之后就不知道怎么写了
        winann:@爱吃草莓的Rock 在-(void)viewWillDisappear:(BOOL)antimated移除
        8d7f845d2cde:@winann 最后移除通知,在dealloc这个方法里,打断点没走啊
        winann:@爱吃草莓的Rock WKWebview有个scrollView属性,可以查看在代理方法:- webView:didFinishNavigation:中,查看webView.scrollView.contentSize,但是,首次加载出来页面之后,这个contentSize获取的经常不准确;还有图片加载的慢,所以也会影响实际的大小,解决方法:可以监听contentSize属性,具体代码如下:
        webView.scrollView.addObserver(self, forKeyPath: "contentSize", options: .New, context: nil)
        然后实现监听的方法如下:
        override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {

        if keyPath == "contentSize" {
        if let newValue = change!["new"] as? NSValue {
        let size = newValue.CGSizeValue()
        // 在这里做你想要的操作
        }
        }
        }
        最后记得在页面消失的时候移除观察者
      • ba43c01d6e14:您好, 现在关于wkwebview这么详细的学习资料很少,谢谢分享。
        我有个问题, 如何才能设置加载的超时时间,wkwebview加载的静态页面字体很小我在configure里设置了那个minimumFontSize也没有效果
        winann:@WiZzzard 我这边没有发现这个问题,我再看看
        ba43c01d6e14:@winann 我设置了超时时间比如是10秒,但是我打印的开始加载到结束加载的时间间隔了20多秒了,我模拟了慢网速的情况下
        winann:@WiZzzard 第一个问题:设置NSMutableURLRequest的timeoutInterval来实现;第二个问题:应该是需要在html里面设置相关参数的,但是我这块比较欠缺,所以不能帮你了,你可以请求:http://news.163.com来测试一下,网易新闻的地址,可以看到效果
      • CharlyZheng:看着内容好多, 暂时没深入到这,
        问一下,怎么给要突出的文字加背景颜色,
        MarkDown小白一枚
        winann:有些编辑器里面会有这些标记的,不过也还好了
        CharlyZheng:@winann 谢了,要的就是这个,,另外这种标记有批量处理的办法吗, 一个一个好慢
        winann:@疯疯癫癫郑成功 是说的上面一些类似的变量那种形式吗?和代码块是差不多的,代码块是三个`,那个用一个就好了,很多MarkDown的工具里面有帮助的
      • Inlight先森:膜拜一下
      • 95114676ad51:我怀疑这个控件是一个极度初期的东西,有很多很多意想不到的BUG。如果我一次性批量加载很多webView ,比如说浏览器的后台加载功能,就会发现有的加载成功了,有的加载失败了。而uiwebView 则不会出现加载失败的情况。
        Auditore:@winann 这是webkit写的 和Safari一个框架 是Apple自家的
        宇宙无敌大强子:@winann 这是苹果自家出的kit,不是第三方的
        winann:@祖师爷 是的,这是一个第三方的框架,而且解决问题的速度有点慢,还有好多其它的坑
      • chad_it:您好 我想问一下
        以简书的首页为例,简书首页的文章点击文章标题在新的标签页打开,点击文章图片在当前标签页打开,如果用WKWebView加载,点击文章图片会打开文章,点击文章标题没反应,应该怎么处理呢?
        谢谢
        chad_it:@winann 感谢
        winann:@不要把世界让给你鄙视的人 在新的标签页打开是没有targetFrame造成的,你可以在
        - webView:decidePolicyForNavigationAction:decisionHandler:代理方法里面处理一下,比方说手动加载,如果你要做浏览器之类的应用,可以根据这个来打开新的标签页。代理方法中的代码如下:
        guard let _ = navigationAction.targetFrame else {
        webView.loadRequest(navigationAction.request)
        decisionHandler(.Allow)
        return
        }

        OC代码也就是判断一下是否targetFrame为空,然后手动加载请求就好了
      • 1f531f4471b1:请问楼主wkwebview中的请求能否通过NSUrlProtocol拦截到?
        leftwater:NSURLProtocol wk拦截不鸟
        1f531f4471b1:@winann 感谢:+1:
        winann:@子矩Allen 这也是一个问题,并且仅当navigationDelegate不设置的时候,每点一次链接才会走一次+ canInitWithRequest:方法,其它方法并不走。如果你要用相关方法的时候,还是用UIWebView吧。WKWebView是有很多坑的
      • 十一岁的加重:后排点赞
        winann:@十一岁的加重 谢谢支持 :smile:
      • 给自己定个小目标:很好,学习了,那用WKWebkit仅仅加载个网页性能真的会很快嘛
        winann:@像雾像雨 😄,WKWebView就是为了解决UIWebView的各种性能问题的,如果适配iOS8 以上,完全可以替代的
        给自己定个小目标:@winann 好的,谢谢,以后我就不用UIWebView 了
        :stuck_out_tongue_closed_eyes:
        winann:@像雾像雨 占用内存会小很多的,你可以加载相同的网页看看。谢谢支持

      本文标题:WKWebView的使用和各种坑的解决方法(OC+Swift)

      本文链接:https://www.haomeiwen.com/subject/gdofjttx.html