IOS浏览控件之WKWebView

作者: i_belive | 来源:发表于2016-11-30 22:30 被阅读2194次

    WkWebView是IOS8中引入的新组件,苹果将UIWebViewDelegate 与 UIWebView 重构成了 14 个类和 3 个协议并引入了不少新的功能和接口。由于一直以来苹果对于WebView内核封锁的程度是令人发指的,WkWebView的引入无疑是另广大开发者兴奋的事

    1、WKWebView介绍

    WKWebView是现代 WebKit API 在 iOS 8 和 OS X Yosemite 应用中的核心部分。它代替了 UIKit 中的UIWebView和 AppKit 中的WebView,提供了统一的跨双平台 API(iOS和OS)。

    2、WKWebView新特性

    1. 在性能、稳定性、功能方面有很大提升(最直观的体现就是加载网页是占用的内存,模拟器加载百度与开源中国网站时,WKWebView占用23M,而UIWebView占用85M)
    2. 和 Safari 相同的 JavaScript 引擎,允许JavaScript的Nitro库加载并使用(UIWebView中限制);
    3. 支持了更多的HTML5特性;
    4. 自诩拥有 60fps 滚动刷新率、内置手势、高效的 app 和 web 信息交换通道

    3、WKWebView使用

    • 在WKWebView,一般都是使用UIWebView,而UIWebView的问题主要有:

      • 内存占用大
      • 效率低
      • 与JS交互比较麻烦
      • 可扩展性低等
    • 使用WKWebView可以有效地解决上述问题。
      示例:

        // 创建webview
        WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds];
        // 创建请求
        NSURLRequest *request =[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.baidu.com"]];
        // 加载网页
        [webView loadRequest:request];
        // 将webView添加到界面
        [self.view addSubview:webView];
    
    • WKWebView操作JS
      • WKWebView加载JS

    //JS文件路径
    NSString *jsPath = [[NSBundle mainBundle] pathForResource:@"demo" ofType:@"js"];
    //读取JS文件内容
    NSString *jsContent = [NSString stringWithContentsOfFile:jsPath encoding:NSUTF8StringEncoding error:nil];
    //创建用户脚本对象,
    //WKUserScriptInjectionTimeAtDocumentStart :HTML文档创建后,完成加载前注入,类似于<head>中
    //WKUserScriptInjectionTimeAtDocumentEnd :HTML文件完成加载后注入,类似于<body>中
    WKUserScript *script = [[WKUserScript alloc] initWithSource:jsContent injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
    //添加用户脚本
    [webView.configuration.userContentController addUserScript:script];

    
    - WKWebView执行JS方法
    

    //执行JS方法
    [webView evaluateJavaScript:@"test()" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
    //result为执行js方法的返回值
    if(error){
    NSLog(@"Success");
    }else{
    NSLog(@"Fail");
    }
    }];

    
    ##4、WKWebView代理方法
    - WKNavigationDelegate 协议
    

    pragma mark - WKNavigationDelegate

    // 页面开始加载时调用

    • (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
      }
      // 内容开始返回时调用
    • (void)webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation {
      }
      // 页面加载完成时调用
    • (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
      }
      // 页面加载失败时调用
    • (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation {
      }
      
        新增的三个代理方法:
      

    // 这个方法是服务器重定向时调用,即 接收到服务器跳转请求之后调用

    • (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(WKNavigation *)navigation {
      }
      // 在收到响应后,决定是否跳转

    • (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
      }
      // 在发送请求之前,决定是否跳转

    • (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
      //需执行decisionHandler的block。
      }

      
      
    • WKUIDelegate 协议

          #pragma mark - WKUIDelegate
        /// 创建一个新的WebView
        - (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
            return nil;
        }
      
        /**
         *  web界面中有弹出警告框时调用
         *
         *  @param webView           实现该代理的webview
         *  @param message           警告框中的内容
         *  @param frame             主窗口
         *  @param completionHandler 警告框消失调用
         */
        - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(void (^)())completionHandler {
            
        }
      
        /// 输入框
        - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler {
            
        }
        /// 确认框
        - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler {
            
        }
        /// 警告框
        - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {
            
        }
      

    5、WKWebView疑难

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

      1. 使用NSURLSession发送一个请求,然后把请求下来的数据当作本地HTML加载
             // 创建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];
      
      1. 使用JavaScript解决WKWebView无法发送POST参数问题(iOS8)
    • iOS9以前版本读取本地HTML的问题
      当使用loadRequest来读取本地的HTML时,WKWebView是无法读取成功的,后台会出现如下的提示:
      Could not create a sandbox extension for /
      原因是WKWebView是不允许通过loadRequest的方法来加载本地根目录的HTML文件。
      而在iOS9的SDK中加入了以下方法来加载本地的HTML文件:
      [WKWebView loadFileURL:allowingReadAccessToURL:]
      但是在iOS9以下的版本是没提供这个便利的方法的。以下为解决方案的思路,就是在iOS9以下版本时,先将本地HTML文件的数据copy到tmp目录中,然后再使用loadRequest来加载。但是如果在HTML中加入了其他资源文件,例如js,css,image等必须一同copy到temp中。这个是最蛋疼的事情了。

          //将文件copy到tmp目录
        - (NSURL *)fileURLForBuggyWKWebView8:(NSURL *)fileURL {
            NSError *error = nil;
            if (!fileURL.fileURL || ![fileURL checkResourceIsReachableAndReturnError:&error]) {
                return nil;
            }
            // Create "/temp/www" directory
            NSFileManager *fileManager= [NSFileManager defaultManager];
            NSURL *temDirURL = [[NSURL fileURLWithPath:NSTemporaryDirectory()] URLByAppendingPathComponent:@"www"];
            [fileManager createDirectoryAtURL:temDirURL withIntermediateDirectories:YES attributes:nil error:&error];
            
            NSURL *dstURL = [temDirURL URLByAppendingPathComponent:fileURL.lastPathComponent];
            // Now copy given file to the temp directory
            [fileManager removeItemAtURL:dstURL error:&error];
            [fileManager copyItemAtURL:fileURL toURL:dstURL error:&error];
            // Files in "/temp/www" load flawlesly :)
            return dstURL;
        }
      
        //调用逻辑
        NSString *path = [[NSBundle mainBundle] pathForResource:@"indexoff" ofType:@"html"];
        if(path){
            if ([[UIDevice currentDevice].systemVersion floatValue] >= 9.0) {
                // iOS9. One year later things are OK.
                NSURL *fileURL = [NSURL fileURLWithPath:path];
                [self.webView loadFileURL:fileURL allowingReadAccessToURL:fileURL];
            } else {
                // iOS8. Things can be workaround-ed
                //   Brave people can do just this
                //   fileURL = try! pathForBuggyWKWebView8(fileURL)
                //   webView.loadRequest(NSURLRequest(URL: fileURL))
                
                NSURL *fileURL = [self.fileHelper fileURLForBuggyWKWebView8:[NSURL fileURLWithPath:path]];
                NSURLRequest *request = [NSURLRequest requestWithURL:fileURL];
                [self.webView loadRequest:request];
            }
        }
      

    问题:http://stackoverflow.com/questions/24882834/wkwebview-not-loading-local-files-under-ios-8
    具体参见:https://github.com/shazron/WKWebViewFIleUrlTest

    ps:在实际测试中,上述方法在iOS8.0系统中也无法成功。如果在iOS8.0系统有成功的请告知下。谢谢!

    相关文章

      网友评论

      • akak:ios8 不是可以loadrequest bundle中的html了吗?

      本文标题:IOS浏览控件之WKWebView

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